<pre class=metadata>
Title: WebGPU
Shortname: webgpu
Level: None
Status: w3c/ED
Group: gpuwg
ED: https://gpuweb.github.io/gpuweb/
TR: https://www.w3.org/TR/webgpu/
Repository: gpuweb/gpuweb
!Participate: <a href="https://github.com/gpuweb/gpuweb/issues/new">File an issue</a> (<a href="https://github.com/gpuweb/gpuweb/issues">open issues</a>)
!Test Suite: <a href="https://github.com/gpuweb/cts">WebGPU CTS</a>

Editor: Kai Ninomiya, Google https://www.google.com, kainino@google.com, w3cid 99487
Editor: Brandon Jones, Google https://www.google.com, bajones@google.com, w3cid 87824
Editor: Jim Blandy, Mozilla https://www.mozilla.com, jimb@mozilla.com, w3cid 131604
Former Editor: Myles C. Maxfield, Apple Inc. https://www.apple.com, mmaxfield@apple.com, w3cid 77180
Former Editor: Dzmitry Malyshau, Mozilla https://www.mozilla.org, dmalyshau@mozilla.com, w3cid 96977
Former Editor: Justin Fan, Apple Inc. https://www.apple.com, justin_fan@apple.com, w3cid 115633
Abstract: WebGPU exposes an API for performing operations, such as rendering and computation, on a Graphics Processing Unit.
Markup Shorthands: markdown yes
Markup Shorthands: dfn yes
Markup Shorthands: idl yes
Markup Shorthands: css no
Assume Explicit For: yes
Deadline: 2025-02-28
</pre>

<pre class=link-defaults>
spec:webidl; type:exception; text:TypeError
spec:webidl; type:exception; text:RangeError
</pre>
<pre class=ignored-specs>
spec:resource-hints;
</pre>
<pre class=anchors>
spec: ECMA-262; urlPrefix: https://tc39.es/ecma262/#
    type: dfn
        text: agent; url: agent
        text: agent cluster; url: sec-agent-clusters
        text: ?; url: sec-returnifabrupt-shorthands
        text: !; url: sec-returnifabrupt-shorthands
        text: Data Block; url: sec-data-blocks
    type: abstract-op
        text: DetachArrayBuffer; url: sec-detacharraybuffer
        text: AllocateArrayBuffer; url: sec-allocatearraybuffer
        text: CreateByteDataBlock; url: sec-createbytedatablock
    type: attribute
        for: ArrayBuffer
            text: [[ArrayBufferDetachKey]]; url: sec-properties-of-the-arraybuffer-instances
spec: HTML; urlPrefix: https://html.spec.whatwg.org/multipage/
    type: dfn
        text: origin-clean; url: canvas.html#concept-canvas-origin-clean
        text: check the usability of the image argument; url: canvas.html#check-the-usability-of-the-image-argument
        text: placeholder canvas element; url: canvas.html#offscreencanvas-placeholder
        text: event loop processing model; url: webappapis.html#event-loop-processing-model
        for: video
            text: intrinsic width; url: media.html#concept-video-intrinsic-width
            text: intrinsic height; url: media.html#concept-video-intrinsic-height
spec: WebCodecs; urlPrefix: https://www.w3.org/TR/webcodecs/#
    type: dfn
        text: Close VideoFrame; url: close-videoframe
spec: WEBGL-1; urlPrefix: https://www.khronos.org/registry/webgl/specs/latest/1.0/#
    type: interface
        text: WebGLRenderingContextBase; url: WEBGLRENDERINGCONTEXTBASE
    type: attribute; for: WebGLRenderingContextBase
        text: drawingBufferColorSpace; url: DOM-WebGLRenderingContext-drawingBufferColorSpace
    type: attribute; for: WebGLRenderingContextBase
        text: drawingBufferWidth; url: DOM-WebGLRenderingContext-drawingBufferWidth
    type: attribute; for: WebGLRenderingContextBase
        text: drawingBufferHeight; url: DOM-WebGLRenderingContext-drawingBufferHeight
    type: dictionary
        text: WebGLContextAttributes; url: WEBGLCONTEXTATTRIBUTES
    type: dfn
        text: WebGL Drawing Buffer; url: THE_DRAWING_BUFFER
spec: WGSL; urlPrefix: https://gpuweb.github.io/gpuweb/wgsl/#
    type: dfn
        text: functions in the shader stage; url: functions-in-a-shader-stage
        text: f16; url: f16
        text: location; url: input-output-locations
        text: blend_src; url: input-output-locations
        text: interpolation; url: interpolation
        text: pipeline-overridable; url: pipeline-overridable
        text: pipeline constant ID; url: pipeline-constant-id
        text: pipeline-overridable constant identifier string; url: pipeline-overridable-constant-identifier-string
        text: pipeline-overridable constant default value; url: pipeline-overridable-constant-default-value
        text: interface of a shader stage; url: interface-of-a-shader
        text: shader execution end; url: shader-execution-end
        text: shader stage output; url: shader-stage-output
        text: shader stage input; url: shader-stage-input
        text: builtin; url: built-in-values
        text: built-in output value; url: built-in-output-value
        text: position builtin; url: built-in-values-position
        text: channel formats; url: channel-formats
        text: invalid memory reference; url: invalid-memory-reference
        text: shader module creation; url: shader-module-creation
        text: pipeline creation; url: pipeline-creation
        text: program error; url: program-error
        text: roundUp; url: roundup
        text: uncategorized error; url: uncategorized-error
        text: shader-creation error; url: shader-creation-error
        text: pipeline-creation error; url: pipeline-creation-error
        text: store type; url: store-type
        text: language extension; url: language-extension
        text: runtime-sized; url: runtime-sized
        text: WGSL floating point conversion; url: floating-point-conversion
        text: WGSL floating point behaviors; url: differences-from-ieee754
        text: WGSL identifier comparison; url: identifier-comparison
        text: WGSL scalar type; url: scalar-types
        text: indeterminate value; url: indeterminate-values
        text: @binding; url: attribute-binding
        text: @group; url: attribute-group
        text: @interpolate; url: interpolate-attr
        text: line break; url: line-break
        text: 64-bit unsigned integer; url: 64-bit-integer
        text: Synchronization Built-in Functions; url: sync-builtin-functions
        text: interpolation-sampling; url: interpolation-sampling
        text: textureSampleLevel; url: texturesamplelevel
        text: interpolation type; url: interpolation-type
        text: use dual source blending; url: use-dual-source-blending
        for: interpolation type
            text: flat; url: interpolation-type-flat
            text: linear; url: interpolation-type-linear
            text: perspective; url: interpolation-type-perspective
        for: address spaces
            text: workgroup; url: address-spaces-workgroup
        for: builtin
            text: sample_mask; url: built-in-values-sample_mask
            text: frag_depth; url: built-in-values-frag_depth
            text: clip_distances; url: built-in-values-clip_distances
            text: workgroup_id; url: built-in-values-workgroup_id
            text: local_invocation_id; url: built-in-values-local_invocation_id
            text: global_invocation_id; url: built-in-values-global_invocation_id
            text: num_workgroups; url: built-in-values-num_workgroups
            text: local_invocation_index; url: built-in-values-local_invocation_index
            text: primitive_index; url: built-in-values-primitive_index
        for: extension
            text: f16; url: extension-f16
            text: clip_distances; url: extension-clip_distances
            text: dual_source_blending; url: extension-dual_source_blending
            text: subgroup; url: extension-subgroups
            text: primitive_index; url: extension-primitive_index
        for: type
            text: sampled texture; url: type-sampled-texture
            text: depth texture; url: type-depth-texture
        for: builtin-value
            text: subgroup size; url: subgroup-size
    type: abstract-op
        text: SizeOf; url: sizeof
spec: Internationalization Glossary; urlPrefix: https://www.w3.org/TR/i18n-glossary/#
    type: dfn
        text: localizable text; url: dfn-localizable-text
spec: Strings on the Web; urlPrefix: https://w3c.github.io/string-meta/#
    type: dfn
        text: best practices for language and direction information; url: bp_and-reco
spec: WebXR; urlPrefix: https://www.w3.org/TR/webxr/
    type: dfn
        text: WebXR session; url: #session
spec: Khronos Data Format Specification; urlPrefix: https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html#
    type: dfn
        text: ASTC compressed formats; url: ASTC
        text: BC compressed formats; url: _compressed_texture_image_formats
        text: ETC2 compressed formats; url: ETC2
</pre>

<pre class=biblio>
{
  "vulkan": {
    "authors": [
      "The Khronos Vulkan Working Group"
    ],
    "href": "https://registry.khronos.org/vulkan/specs/1.3/html/vkspec.html",
    "title": "Vulkan 1.3",
    "publisher": "Khronos",
    "deliveredBy": [
      "https://www.khronos.org/"
    ]
  }
}
</pre>

<link rel="icon" type="image/png" sizes="32x32" href="img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="img/favicon-96x96.png">

<style>
/* Displays the WebGPU logo next to the W3C logo */
.head h1:after {
    content: url(img/logo.png);
    content: url(img/logo.png) / "WebGPU logo"; /* alt text */
    float: right;
}

/* Make <dl> blocks more distinct from their surroundings. */
main dl:not(.switch) {
    border-left: thin solid #f3e48c;
    padding-left: .5em;
}

/* Tweak default "switch" statement layout to work better for our style. */
dl.switch dt {
    margin-left: -0.5em;
}
dl.switch dd {
    margin-left: 0em;
}

/* <p> by default has these margins. Update ul/ol/dl to match,
 * since they are also put in places where paragraphs go. */
p, ul, ol, dl {
    margin: 1em 0;
}

/* Styling for "editorial" issues */
.note.editorial {
    border-color: var(--note-editorial-color);
    background: var(--note-editorial-bg);
}
.note.editorial > .marker {
    color: var(--note-editorial-color);
}

/* Some of our SVGs aren't responsive to light/dark mode, so they're opaque with a
 * white or black background. Rounded corners make them a bit less jarring. */
object[type="image/svg+xml"] {
    border-radius: .5em;
}

/* The new spec template doesn't put a box around algorithms anymore. */
/* Add a similar box for Valid Usage requirements. */
div.algorithm, div.validusage {
    margin: .5em 0;
    padding: .5em;
    border-width: thin;
    border-style: solid;
    border-radius: .5em;
}
div.validusage {
    border-color: #88e;
}
div.algorithm {
    border-color: #ddd;
}
/*
 * If the Valid Usage requirements are the first child of a *-timeline block give it a larger top
 * margin to prevent the block labels from overlapping.
 */
.content-timeline>.validusage:first-child,
.device-timeline>.validusage:first-child,
.queue-timeline>.validusage:first-child {
    margin-top: 1.5em;
}

/*
 * Boxes for steps that occur on a particular timeline.
 */
div.content-timeline, div.device-timeline, div.queue-timeline {
    padding: .5em;
    border-radius: .5em;
}
.content-timeline {
    background: rgba(0, 255, 0, 0.05);
    background: var(--tint-green);
}
.device-timeline {
    background: rgba(255, 0, 0, 0.05);
    background: var(--tint-red);
}
.queue-timeline {
    background: rgba(255, 0, 255, 0.05);
    background: var(--tint-purple);
}

span[data-timeline],
dfn[data-timeline],
var[data-timeline],
a[data-timeline],
[href="#content-timeline"],
[href="#device-timeline"],
[href="#queue-timeline"] {
    border-style: solid;
    border-radius: .3em;
    border-width: 0 0 .2em 0;
    text-decoration: none;
}
dl[data-timeline],
div[data-timeline] {
    border-style: solid;
    border-radius: 1em 0 0 1em;
    padding: .5em;
}
dl[data-timeline] {
    border-width: 0 0 0 .25em !important;
}
div[data-timeline] {
    border-width: 1px 0 0 .75em;
}
div.algorithm[data-timeline] {
    border-width: 1px 1px 1px .75em;
}
div.algorithm > div[data-timeline] {
    /* Remove unnecessary padding in this common case. Makes the border of the
     * timeline box line up with the border of the surrounding algorithm box. */
    margin-left: calc(-.5em - 1px);
    margin-right: calc(-.5em - 1px);
}

/*
 * Coloring for steps and other stuff on particular timelines.
 */
[data-timeline="content"],
[href="#content-timeline"] {
    border-color: #00cc00 !important;
}
[data-timeline="device"],
[href="#device-timeline"] {
    border-color: #880000 !important;
}
[data-timeline="queue"],
[href="#queue-timeline"] {
    border-color: #c900f1 !important;
}
[data-timeline="const"] {
    /* 186 is perceptual 50% gray, so works for both light and dark modes. */
    border-color: rgb(186 186 186 / 30%) !important;
}

/*
 * Stylistic labels, for clarity of presentation of these blocks.
 *
 * NOTE: This text is non-accessible (no alt text) and non-selectable;
 * surrounding text must also explain the context.
 */
.validusage,
.content-timeline, .device-timeline, .queue-timeline,
[data-timeline] {
    position: relative;
}
.validusage::before,
.content-timeline::before,
.device-timeline::before,
.queue-timeline::before {
    font-weight: bold;
    font-style: italic;
    font-size: 1.3rem;
    color: rgba(0, 0, 0, 0.15);
    color: var(--watermark-text);
    position: absolute;
    right: .3em;
    top: -.1em;
}
.validusage::before {
    content: "Valid Usage";
    content: "Valid Usage" / ""; /* No alt text, see above */
}
.content-timeline::before {
    content: "Content Timeline";
    content: "Content Timeline" / ""; /* No alt text, see above */
}
.device-timeline::before {
    content: "Device Timeline";
    content: "Device Timeline" / ""; /* No alt text, see above */
}
.queue-timeline::before {
    content: "Queue Timeline";
    content: "Queue Timeline" / ""; /* No alt text, see above */
}

/*
 * Ensure that argumentdef blocks don't overflow algorithm section borders. This is made far harder
 * than it needs to be because the top-level W3C stylesheet has several @media + min-width variants
 * that mark themselves as !important and then proceed to do the wrong thing.
 */
@media screen and (min-width: 78em) {
    body:not(.toc-inline) .algorithm .overlarge {
        margin-right: auto !important;
    }
}
@media screen and (min-width: 90em) {
    body:not(.toc-inline) .algorithm .overlarge {
        margin-right: auto !important;
    }
}
.algorithm .overlarge {
    margin-right: auto !important;
}

/*
 * The default argumentdef style has a caption that doesn't suit this spec's
 * formatting particularly well. Hide it.
 */
.algorithm .argumentdef {
    margin-top: 0;
}
.algorithm .argumentdef > caption {
    display: none;
}

/*
 * Argumentdef is also too wide given our typenames, so make some tweaks.
 */
/* Move border to tr and reduce padding. */
.algorithm .argumentdef td,
.algorithm .argumentdef th {
    padding: .5em;
    border: none;
}
.algorithm .argumentdef > tbody > tr {
    border-top: thin solid var(--datacell-border);
}
/* Compress "Nullable" and "Optional" and remove horizontal padding. */
.algorithm .argumentdef > thead > tr > :nth-child(3),
.algorithm .argumentdef > thead > tr > :nth-child(4) {
    font-family: monospace;
    max-width: 4ch;
    word-wrap: break-word;
}
.algorithm .argumentdef tr > :nth-child(3),
.algorithm .argumentdef tr > :nth-child(4) {
    padding-left: .5em;
    padding-right: 0;
}

/* Wrap Parameter and Type onto separate rows. */
.algorithm .argumentdef > thead > tr > th:nth-child(1),
.algorithm .argumentdef > tbody > tr > td:nth-child(1) {
    padding-right: .5em;
    float: left;
}
.algorithm .argumentdef > thead > tr > th:nth-child(2),
.algorithm .argumentdef > tbody > tr > td:nth-child(2) {
    padding-left: .5em;
    float: right;
}
.algorithm .argumentdef > thead > tr > th {
    vertical-align: top;
}

/*
 * Add vertical lines to demarcate multi-column cells.
 */
table.data td,
table.data th {
    /* Only one side is needed because there are no outer borders on the tables.
     * 186 is perceptual 50% gray, so works for both light and dark modes. */
    border-left: 1px solid rgb(186 186 186 / 30%);
}

table.data tr.row-continuation td[colspan],
table.data tr.row-continuation th[colspan] {
    text-align: unset;
}

table.data tr.row-continuation td,
table.data tr.row-continuation th {
    border-top: none;
}

/*
 * Vertical class for table columns. (Can't use th.vertical directly because of a Safari bug.)
 */
th.vertical {
    vertical-align: top;
}
th.vertical span {
    writing-mode: vertical-rl;
    white-space: nowrap;
}

/*
 * Sticky table headers.
 */
.overlarge {
    /* position: sticky doesn't work inside scrollable elements. */
    overflow-x: unset;
}
thead.stickyheader, th.stickyheader {
    position: sticky;
    top: 0;
    background: #f8f8f8;
    background: var(--stickyheader-background);
}

/*
 * Coordinate System Figures
 */

.coordinate-diagram {
    width: 500px;
}

/*
 * Light-mode and dark-mode colors
 */
:root {
    --watermark-text: rgba(0, 0, 0, 15%);
    --stickyheader-background: #f8f8f8;
    --tint-red: rgba(255, 0, 0, 6%);
    --tint-green: rgba(0, 255, 0, 10%);
    --tint-blue: rgba(0, 0, 255, 5%);
    --tint-purple: rgba(255, 0, 255, 5%);
    --note-editorial-color: #c06e00;
    --note-editorial-bg: #ffeedd;
}
body.darkmode {
    --watermark-text: rgba(255, 255, 255, 25%);
    --stickyheader-background: #181818;
    --tint-red: rgba(255, 0, 0, 20%);
    --tint-green: rgba(0, 255, 0, 18%);
    --tint-blue: rgba(0, 130, 255, 24%);
    --tint-purple: rgba(255, 0, 255, 22%);
    --note-editorial-color: #ff9100;
    --note-editorial-bg: var(--borderedblock-bg);
}
</style>

# Introduction # {#intro}

*This section is non-normative.*

[Graphics Processing Units](https://en.wikipedia.org/wiki/Graphics_processing_unit), or GPUs for short,
have been essential in enabling rich rendering and computational applications in personal computing.
WebGPU is an API that exposes the capabilities of GPU hardware for the Web.
The API is designed from the ground up to efficiently map to (post-2014) native GPU APIs.
WebGPU is not related to [WebGL](https://www.khronos.org/webgl/) and does not explicitly target OpenGL ES.

WebGPU sees physical GPU hardware as {{GPUAdapter}}s. It provides a connection to an adapter via
{{GPUDevice}}, which manages resources, and the device's {{GPUQueue}}s, which execute commands.
{{GPUDevice}} may have its own memory with high-speed access to the processing units.
{{GPUBuffer}} and {{GPUTexture}} are the <dfn dfn>physical resources</dfn> backed by GPU memory.
{{GPUCommandBuffer}} and {{GPURenderBundle}} are containers for user-recorded commands.
{{GPUShaderModule}} contains [=shader=] code. The other resources,
such as {{GPUSampler}} or {{GPUBindGroup}}, configure the way [=physical resources=] are used by the GPU.

GPUs execute commands encoded in {{GPUCommandBuffer}}s by feeding data through a [=pipeline=],
which is a mix of fixed-function and programmable stages. Programmable stages execute
<dfn dfn>shaders</dfn>, which are special programs designed to run on GPU hardware.
Most of the state of a [=pipeline=] is defined by
a {{GPURenderPipeline}} or a {{GPUComputePipeline}} object. The state not included
in these [=pipeline=] objects is set during encoding with commands,
such as {{GPUCommandEncoder/beginRenderPass()}} or {{GPURenderPassEncoder/setBlendConstant()}}.

<pre class=include>
path: sections/privacy-and-security.bs
</pre>

# Fundamentals # {#fundamentals}

## Conventions ## {#api-conventions}

### Syntactic Shorthands ### {#shorthands}

In this specification, the following syntactic shorthands are used:

: The `.` ("dot") syntax, common in programming languages.
::
    The phrasing "`Foo.Bar`" means "the `Bar` member of the value (or interface) `Foo`."
    If `Foo` is an [=ordered map=] and `Bar` does not [=map/exist=] in `Foo`, returns `undefined`.

    The phrasing "`Foo.Bar` is [=map/exist|provided=]" means
    "the `Bar` member [=map/exists=] in the [=map=] value `Foo`"

: The `?.` ("optional chaining") syntax, adopted from JavaScript.
::
    The phrasing "`Foo?.Bar`" means
    "if `Foo` is `null` or `undefined` or `Bar` does not [=map/exist=] in `Foo`, `undefined`; otherwise, `Foo.Bar`".

    For example, where `buffer` is a {{GPUBuffer}}, `buffer?.\[[device]].\[[adapter]]` means
    "if `buffer` is `null` or `undefined`, then `undefined`; otherwise,
    the `\[[adapter]]` internal slot of the `\[[device]]` internal slot of `buffer`.

: The `??` ("nullish coalescing") syntax, adopted from JavaScript.
::
    The phrasing "`x` ?? `y`" means "`x`, if `x` is not null or undefined, and `y` otherwise".

: <dfn dfn>slot-backed attribute</dfn>
::
    A WebIDL attribute which is backed by an internal slot of the same name.
    It may or may not be mutable.

<h4 id=webgpu-objects>WebGPU Objects
<span id=webgpu-internal-objects></span>
</h4>

A <dfn dfn>WebGPU object</dfn> consists of a [=WebGPU Interface=] and an [=internal object=].

The <dfn dfn>WebGPU interface</dfn> defines the public interface and state of the [=WebGPU object=].
It can be used on the [=content timeline=] where it was created, where it is a JavaScript-exposed
WebIDL interface.

Any interface which includes <dfn interface>GPUObjectBase</dfn> is a [=WebGPU interface=].

The <dfn dfn>internal object</dfn> tracks the state of the [=WebGPU object=] on the [=device timeline=].
All reads/writes to the mutable state of an [=internal object=] occur from steps executing on a
single well-ordered [=device timeline=].

<!-- POSTV1(multithreading) tentative text:
These steps may have been issued from a [=content timeline=] algorithm on any of multiple [=agents=].
Note: An "[=agent=]" refers to a JavaScript "thread" (i.e. main thread, or Web Worker).
-->

The following special property types can be defined on [=WebGPU objects=]:

: <dfn dfn data-timeline=const>immutable property</dfn>
::
    A read-only slot set during initialization of the object. It can be accessed from any timeline.

    Note: Since the slot is immutable, implementations may have a copy on multiple timelines, as needed.
    [=Immutable properties=] are defined in this way to avoid describing multiple copies in this spec.

    If named `[[with brackets]]`, it is an internal slot.<br/>
    If named `withoutBrackets`, it is a `readonly` [=slot-backed attribute=] of the [=WebGPU interface=].

: <dfn dfn data-timeline=content>content timeline property</dfn>
::
    A property which is only accessible from the [=content timeline=]
    where the object was created.

    If named `[[with brackets]]`, it is an internal slot.<br/>
    If named `withoutBrackets`, it is a [=slot-backed attribute=] of the [=WebGPU interface=].

: <dfn dfn data-timeline=device>device timeline property</dfn>
::
    A property which tracks state of the [=internal object=] and is only accessible from the
    [=device timeline=] where the object was created. [=device timeline properties=] may be mutable.

    [=Device timeline properties=] are named `[[with brackets]]`, and are internal slots.

: <dfn dfn data-timeline=queue>queue timeline property</dfn>
::
    A property which tracks state of the [=internal object=] and is only accessible from the
    [=queue timeline=] where the object was created. [=queue timeline properties=] may be mutable.

    [=Queue timeline properties=] are named `[[with brackets]]`, and are internal slots.

<script type=idl>
interface mixin GPUObjectBase {
    attribute USVString label;
};
</script>

<div algorithm data-timeline=content>
    To <dfn abstract-op>create a new WebGPU object</dfn>({{GPUObjectBase}} |parent|,
    interface |T|, {{GPUObjectDescriptorBase}} |descriptor|)
    (where |T| extends {{GPUObjectBase}}), run the following [=content timeline=] steps:

    1. Let |device| be |parent|.{{GPUObjectBase/[[device]]}}.
    1. Let |object| be a new instance of |T|.
    1. Set |object|.{{GPUObjectBase/[[device]]}} to |device|.
    1. Set |object|.{{GPUObjectBase/label}} to |descriptor|.{{GPUObjectDescriptorBase/label}}.
    1. Return |object|.
</div>

{{GPUObjectBase}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUObjectBase data-timeline=const>
    : <dfn>\[[device]]</dfn>, of type [=device=], readonly
    ::
        The [=device=] that owns the [=internal object=].

        Operations on the contents of this object [=assert=] they are running on the
        [=device timeline=], and that the device is [$valid$].
</dl>

{{GPUObjectBase}} has the following [=content timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUObjectBase data-timeline=content>
    : <dfn>label</dfn>
    ::
        A developer-provided label which is used in an [=implementation-defined=] way.
        It can be used by the browser, OS, or other tools to help
        identify the underlying [=internal object=] to the developer.
        Examples include displaying the label in {{GPUError}} messages, console warnings,
        browser developer tools, and platform debugging utilities.

        <div class=note heading>
            Implementations **should** use labels to enhance error messages by using them to
            identify WebGPU objects.

            However, this need not be the only way of identifying objects:
            implementations **should** also use other available information,
            especially when no label is available. For example:

            - The label of the parent {{GPUTexture}} when printing a {{GPUTextureView}}.
            - The label of the parent {{GPUCommandEncoder}} when printing a
                {{GPURenderPassEncoder}} or {{GPUComputePassEncoder}}.
            - The label of the source {{GPUCommandEncoder}} when printing a {{GPUCommandBuffer}}.
            - The label of the source {{GPURenderBundleEncoder}} when printing a {{GPURenderBundle}}.
        </div>

        <div class=note heading>
            The {{GPUObjectBase/label}} is a property of the {{GPUObjectBase}}.
            Two {{GPUObjectBase}} "wrapper" objects have completely separate label states,
            even if they refer to the same underlying object
            (for example returned by {{GPUPipelineBase/getBindGroupLayout()}}).
            The {{GPUObjectBase/label}} property will not change except by being set from JavaScript.

            This means one underlying object could be associated with multiple labels.
            This specification does not define how the label is propagated to the [=device timeline=].
            How labels are used is completely [=implementation-defined=]: error messages could
            show the most recently set label, all known labels, or no labels at all.

            It is defined as a {{USVString}} because some user agents may
            supply it to the debug facilities of the underlying native APIs.
        </div>
</dl>

{{GPUObjectBase}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUObjectBase data-timeline=device>
    : <dfn>\[[valid]]</dfn>, of type {{boolean}}, initially `true`.
    ::
        If `true`, indicates that the [=internal object=] is valid to use.
</dl>

<div class=note heading>
    Ideally [=WebGPU interfaces=] should not prevent their parent objects, such as the
    {{GPUObjectBase/[[device]]}} that owns them, from being garbage collected. This cannot be
    guaranteed, however, as holding a strong reference to a parent object may be required in some
    implementations.

    As a result, developers should assume that a [=WebGPU interface=] may not be garbage collected
    until all child objects of that interface have also been garbage collected. This may cause some
    resources to remain allocated longer than anticipated.

    Calling the `destroy` method on a [=WebGPU interface=] (such as
    {{GPUDevice}}.{{GPUDevice/destroy()}} or {{GPUBuffer}}.{{GPUBuffer/destroy()}}) should be
    favored over relying on garbage collection if predictable release of allocated resources is
    needed.
</div>

### Object Descriptors ### {#object-descriptors}

An <dfn dfn>object descriptor</dfn> holds the information needed to create an object,
which is typically done via one of the `create*` methods of {{GPUDevice}}.

<script type=idl>
dictionary GPUObjectDescriptorBase {
    USVString label = "";
};
</script>

{{GPUObjectDescriptorBase}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUObjectDescriptorBase>
    : <dfn>label</dfn>
    ::
        The initial value of {{GPUObjectBase/label|GPUObjectBase.label}}.
</dl>

## Asynchrony ## {#asynchrony}

### Invalid Internal Objects &amp; Contagious Invalidity ### {#invalidity}

Object creation operations in WebGPU don't return promises, but nonetheless are internally
asynchronous. Returned objects refer to [=internal objects=] which are manipulated on a
[=device timeline=]. Rather than fail with exceptions or rejections, most errors that occur on a
[=device timeline=] are communicated through {{GPUError}}s generated on the associated [=device=].

[=Internal objects=] are either [$valid$] or [$invalid$].
An [$invalid$] object will never become [$valid$] at a later time,
but some [$valid$] objects may be [$invalidated$].

Objects are [$invalid$] from creation if it wasn't possible to create them.
This can happen, for example, if the [=object descriptor=] doesn't describe a valid
object, or if there is not enough memory to allocate a resource.
It can also happen if an object is created with or from another invalid object
(for example calling {{GPUTexture/createView()}} on an invalid {{GPUTexture}})
(for example the {{GPUTexture}} of a {{GPUTexture/createView()}} call):
this case is referred to as <dfn dfn noexport>contagious invalidity</dfn>.

[=Internal objects=] of *most* types cannot become [$invalid$] after they are created, but still
may become unusable, e.g. if the owning device is [=lose the device|lost=] or
{{GPUDevice/destroy()|destroyed}}, or the object has a special internal state,
like buffer state "[=GPUBuffer/[[internal state]]/destroyed=]".

[=Internal objects=] of some types *can* become [$invalid$] after they are created; specifically,
[=devices=], [=adapters=], {{GPUCommandBuffer}}s, and command/pass/bundle encoders.

<div algorithm data-timeline=device>
    A given {{GPUObjectBase}} |object| is <dfn abstract-op>valid</dfn> if
    |object|.{{GPUObjectBase/[[valid]]}} is `true`.
</div>

<div algorithm data-timeline=device>
    A given {{GPUObjectBase}} |object| is <dfn abstract-op>invalid</dfn> if
    |object|.{{GPUObjectBase/[[valid]]}} is `false`.
</div>

<div algorithm data-timeline=device>
    A given {{GPUObjectBase}} |object| is <dfn abstract-op>valid to use with</dfn>
    a |targetObject| if the all of the requirements in the following [=device timeline=] steps are met:

    <div class=validusage>
        - |object|.{{GPUObjectBase/[[valid]]}} must be `true`.
        - |object|.{{GPUObjectBase/[[device]]}}.{{GPUObjectBase/[[valid]]}} must be `true`.
        - |object|.{{GPUObjectBase/[[device]]}} must equal |targetObject|.{{GPUObjectBase/[[device]]}}.
    </div>
</div>

<div algorithm data-timeline=device>
    To <dfn abstract-op lt='invalidate|Invalidate|invalidated|invalidates'>invalidate</dfn> a {{GPUObjectBase}}
    |object|, run the following [=device timeline=] steps:

    1. |object|.{{GPUObjectBase/[[valid]]}} to `false`.
</div>

### Promise Ordering ### {#promise-ordering}

Several operations in WebGPU return promises.

- {{GPU}}.{{GPU/requestAdapter()}}
- {{GPUAdapter}}.{{GPUAdapter/requestDevice()}}
- {{GPUDevice}}.{{GPUDevice/createComputePipelineAsync()}}
- {{GPUDevice}}.{{GPUDevice/createRenderPipelineAsync()}}
- {{GPUShaderModule}}.{{GPUShaderModule/getCompilationInfo()}}
- {{GPUQueue}}.{{GPUQueue/onSubmittedWorkDone()}}
- {{GPUBuffer}}.{{GPUBuffer/mapAsync()}}
- {{GPUDevice}}.{{GPUDevice/lost}}
- {{GPUDevice}}.{{GPUDevice/popErrorScope()}}

WebGPU does not make any guarantees about the order in which these promises settle
(resolve or reject), except for the following:

- <div algorithm="onSubmittedWorkDone ordering" data-timeline=content>
        For some {{GPUQueue}} |q|,
        if |p1| = |q|.{{GPUQueue/onSubmittedWorkDone()}} is called before
        |p2| = |q|.{{GPUQueue/onSubmittedWorkDone()}},
        then |p1| must settle before |p2|.
    </div>
- <div algorithm="mapAsync-onSubmittedWorkDone ordering" data-timeline=content>
        For some {{GPUQueue}} |q| and {{GPUBuffer}} |b| on the same {{GPUDevice}},
        if |p1| = |b|.{{GPUBuffer/mapAsync()}} is called before
        |p2| = |q|.{{GPUQueue/onSubmittedWorkDone()}},
        then |p1| must settle before |p2|.

        <!-- POSTV1(multi-queue):
        and |b|'s most recent usage was either another mapping or an exclusive usage on |q| -->
    </div>

Applications must not rely on any other promise settlement ordering.

## Coordinate Systems ## {#coordinate-systems}

Rendering operations use the following coordinate systems:

* <dfn lt="NDC">Normalized device coordinates</dfn> (or NDC) have three dimensions, where:
    * -1.0 &leq; x &leq; 1.0
    * -1.0 &leq; y &leq; 1.0
    * 0.0 &leq; z &leq; 1.0
    * The bottom-left corner is at (-1.0, -1.0, z).

    <figure>
        <figcaption>Normalized device coordinates.</figcaption>
        <object class=coordinate-diagram type="image/svg+xml" data="img/normalized-device-coordinates.svg"></object>
    </figure>

    Note: Whether `z = 0` or `z = 1` is treated as the near plane is application specific. The above diagram presents
    `z = 0` as the near plane but the observed behavior is determined by a combination of the projection matrices
    used by shaders, the {{GPURenderPassDepthStencilAttachment/depthClearValue}}, and the
    {{GPUDepthStencilState/depthCompare}} function.
* <dfn noexport>Clip space coordinates</dfn> have four dimensions: (x, y, z, w)
    * Clip space coordinates are used for the the [=clip position=] of a vertex (i.e. the [=position builtin|position=] output of a vertex shader),
        and for the [=clip volume=].
    * [=NDC|Normalized device coordinates=] and clip space coordinates are related as follows:
        If point *p = (p.x, p.y, p.z, p.w)* is in the [=clip volume=], then its normalized device coordinates are (*p.x* &divide; *p.w*, *p.y* &divide; *p.w*, *p.z* &divide; *p.w*).
* <dfn noexport>Framebuffer coordinates</dfn> address the pixels in the [=framebuffer=]
    * They have two dimensions.
    * Each pixel extends 1 unit in x and y dimensions.
    * The top-left corner is at (0.0, 0.0).
    * x increases to the right.
    * y increases down.
    * See [[#render-passes]] and [[#rasterization]].

    <figure>
        <figcaption>Framebuffer coordinates.</figcaption>
        <object class=coordinate-diagram type="image/svg+xml" data="img/framebuffer-coordinates.svg"></object>
    </figure>
* <dfn>Viewport coordinates</dfn> combine [=framebuffer coordinates=] in x and y dimensions,
    with depth in z.
    * Normally 0.0 &leq; z &leq; 1.0, but this can be modified by setting {{RenderState/[[viewport]]}}.`minDepth` and `maxDepth` via
        {{GPURenderPassEncoder/setViewport()}}
* <dfn noexport>Fragment coordinates</dfn> match [=viewport coordinates=].
* <dfn noexport>Texture coordinates</dfn>, sometimes called "UV coordinates" in 2D, are used to sample
    textures and have a number of components matching the {{GPUTextureDimension|texture dimension}}.
    * 0 &leq; u &leq; 1.0
    * 0 &leq; v &leq; 1.0
    * 0 &leq; w &leq; 1.0
    * (0.0, 0.0, 0.0) is in the first texel in texture memory address order.
    * (1.0, 1.0, 1.0) is in the last texel texture memory address order.

    <figure>
        <figcaption>2D Texture coordinates.</figcaption>
        <object class=coordinate-diagram type="image/svg+xml" data="img/uv-coordinates.svg"></object>
    </figure>
* <dfn noexport>Window coordinates</dfn>, or <dfn noexport>present coordinates</dfn>,
    match [=framebuffer coordinates=], and are used when interacting with
    an external display or conceptually similar interface.

Note: WebGPU's coordinate systems match DirectX's coordinate systems in a graphics pipeline.

## Programming Model ## {#programming-model}

### Timelines ### {#programming-model-timelines}

WebGPU's behavior is described in terms of "timelines".
Each operation (defined as algorithms) occurs on a timeline.
Timelines clearly define both the order of operations, and which state is
available to which operations.

Note:
This "timeline" model describes the constraints of the multi-process models of
browser engines (typically with a "content process" and "GPU process"), as well
as the GPU itself as a separate execution unit in many implementations.
Implementing WebGPU does not require timelines to execute in parallel, so does
not require multiple processes, or even multiple threads.
(It does require concurrency for cases like [$get a copy of the image contents of a context$]
which synchronously blocks on another timeline to complete.)

: <dfn dfn>Content timeline</dfn>
:: Associated with the execution of the Web script.
    It includes calling all methods described by this specification.

    To issue steps to the content timeline from an operation on {{GPUDevice}} `device`,
    [$queue a global task for GPUDevice$] `device` with those steps.

: <dfn dfn>Device timeline</dfn>
:: Associated with the GPU device operations
    that are issued by the user agent.
    It includes creation of adapters, devices, and GPU resources
    and state objects, which are typically synchronous operations from the point
    of view of the user agent part that controls the GPU,
    but can live in a separate OS process.

: <dfn dfn>Queue timeline</dfn>
:: Associated with the execution of operations
    on the compute units of the GPU. It includes actual draw, copy,
    and compute jobs that run on the GPU.

: <dfn dfn>Timeline-agnostic</dfn>
:: Associated with any of the above timelines

    Steps may be issued to any timeline if they only operate on [=immutable properties=] or
    arguments passed from the calling steps.

<div class=example style="background: var(--bg)">
    The following show the styling of steps and values associated with each timeline.
    This styling is non-normative; the specification text always describes the association.

    <dl data-timeline=const>
        : <dfn>Immutable value example term</dfn> definition
        :: Can be used on any timeline.
    </dl>

    <dl data-timeline=content>
        : <dfn>Content-timeline example term</dfn> definition
        :: Can only be used on the [=content timeline=].
    </dl>

    <dl data-timeline=device>
        : <dfn>Device-timeline example term</dfn> definition
        :: Can only be used on the [=device timeline=].
    </dl>

    <dl data-timeline=queue>
        : <dfn>Queue-timeline example term</dfn> definition
        :: Can only be used on the [=queue timeline=].
    </dl>

    <div class=algorithm>
        <div data-timeline=const>
            Steps which are [=timeline-agnostic=] look like this.

            [=Immutable value example term=] usage.
        </div>
        <div data-timeline=content>
            Steps executed on the [=content timeline=] look like this.

            [=Immutable value example term=] usage.
            [=Content-timeline example term=] usage.
        </div>
        <div data-timeline=device>
            Steps executed on the [=device timeline=] look like this.

            [=Immutable value example term=] usage.
            [=Device-timeline example term=] usage.
        </div>
        <div data-timeline=queue>
            Steps executed on the [=queue timeline=] look like this.

            [=Immutable value example term=] usage.
            [=Queue-timeline example term=] usage.
        </div>
    </div>
</div>

In this specification, asynchronous operations are used when the return value
depends on work that happens on any timeline other than the [=Content timeline=].
They are represented by promises and events in API.

<div class=example>
    {{GPUComputePassEncoder/dispatchWorkgroups()|GPUComputePassEncoder.dispatchWorkgroups()}}:

    1. User encodes a `dispatchWorkgroups` command by calling a method of the
        {{GPUComputePassEncoder}} which happens on the [=Content timeline=].
    1. User issues {{GPUQueue/submit(commandBuffers)|GPUQueue.submit()}} that hands over
        the {{GPUCommandBuffer}} to the user agent, which processes it
        on the [=Device timeline=] by calling the OS driver to do a low-level submission.
    1. The submit gets dispatched by the GPU invocation scheduler onto the
        actual compute units for execution, which happens on the [=Queue timeline=].
</div>

<div class=example>
    {{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer()}}:

    1. User fills out a {{GPUBufferDescriptor}} and creates a {{GPUBuffer}} with it,
        which happens on the [=Content timeline=].
    2. User agent creates a low-level buffer on the [=Device timeline=].
</div>

<div class=example>
    {{GPUBuffer/mapAsync()|GPUBuffer.mapAsync()}}:

    1. User requests to map a {{GPUBuffer}} on the [=Content timeline=] and
        gets a promise in return.
    2. User agent checks if the buffer is currently used by the GPU
        and makes a reminder to itself to check back when this usage is over.
    3. After the GPU operating on [=Queue timeline=] is done using the buffer,
        the user agent maps it to memory and [=resolves=] the promise.
</div>

### Memory Model ### {#programming-model-memory}

*This section is non-normative.*

Once a {{GPUDevice}} has been obtained during an application initialization routine,
we can describe the <dfn dfn>WebGPU platform</dfn> as consisting of the following layers:

1. User agent implementing the specification.
1. Operating system with low-level native API drivers for this device.
1. Actual CPU and GPU hardware.

Each layer of the [=WebGPU platform=] may have different memory types
that the user agent needs to consider when implementing the specification:

- The script-owned memory, such as an {{ArrayBuffer}} created by the script,
    is generally not accessible by a GPU driver.
- A user agent may have different processes responsible for running
    the content and communication to the GPU driver.
    In this case, it uses inter-process shared memory to transfer data.
- Dedicated GPUs have their own memory with high bandwidth,
    while integrated GPUs typically share memory with the system.

Most [=physical resources=] are allocated in the memory of type
that is efficient for computation or rendering by the GPU.
When the user needs to provide new data to the GPU,
the data may first need to cross the process boundary in order to reach
the user agent part that communicates with the GPU driver.
Then it may need to be made visible to the driver,
which sometimes requires a copy into driver-allocated staging memory.
Finally, it may need to be transferred to the dedicated GPU memory,
potentially changing the internal layout into one
that is most efficient for GPUs to operate on.

All of these transitions are done by the WebGPU implementation of the user agent.

Note: This example describes the worst case, while in practice
the implementation may not need to cross the process boundary,
or may be able to expose the driver-managed memory directly to
the user behind an `ArrayBuffer`, thus avoiding any data copies.

### Resource Usages ### {#programming-model-resource-usages}

A [=physical resource=] can be used with an <dfn dfn>internal usage</dfn> by a [=GPU command=]:

<dl dfn-type=dfn dfn-for="internal usage">
    : <dfn>input</dfn>
    :: Buffer with input data for draw or dispatch calls. Preserves the contents.
        Allowed by buffer {{GPUBufferUsage/INDEX}}, buffer {{GPUBufferUsage/VERTEX}}, or buffer {{GPUBufferUsage/INDIRECT}}.
    : <dfn>constant</dfn>
    ::  Resource bindings that are constant from the shader point of view. Preserves the contents.
        Allowed by buffer {{GPUBufferUsage/UNIFORM}} or texture {{GPUTextureUsage/TEXTURE_BINDING}}.
    : <dfn>storage</dfn>
    ::  Read/write storage resource binding.
        Allowed by buffer {{GPUBufferUsage/STORAGE}} or texture {{GPUTextureUsage/STORAGE_BINDING}}.
    : <dfn>storage-read</dfn>
    ::  Read-only storage resource bindings. Preserves the contents.
        Allowed by buffer {{GPUBufferUsage/STORAGE}} or texture {{GPUTextureUsage/STORAGE_BINDING}}.
    : <dfn>attachment</dfn>
    :: Texture used as a read/write output attachment or
        write-only resolve target in a render pass.
        Allowed by texture {{GPUTextureUsage/RENDER_ATTACHMENT}}.
    : <dfn>attachment-read</dfn>
    ::  Texture used as a read-only attachment in a render pass. Preserves the contents.
        Allowed by texture {{GPUTextureUsage/RENDER_ATTACHMENT}}.
</dl>

We define <dfn dfn>subresource</dfn> to be either a whole buffer, or a [=texture subresource=].

<div algorithm="compatible usage list">
    Some [=internal usages=] are compatible with others. A [=subresource=] can be in a state
    that combines multiple usages together. We consider a list |U| to be
    a <dfn dfn>compatible usage list</dfn> if (and only if) it satisfies any of the following rules:

    - Each usage in |U| is [=internal usage/input=], [=internal usage/constant=], [=internal usage/storage-read=], or [=internal usage/attachment-read=].

    - Each usage in |U| is [=internal usage/storage=].

        Multiple such usages are allowed even though they are writable.
        This is the [=usage scope storage exception=].

    - Each usage in |U| is [=internal usage/attachment=].

        Multiple such usages are allowed even though they are writable.
        This is the [=usage scope attachment exception=].
</div>

Enforcing that the usages are only combined into a [=compatible usage list=]
allows the API to limit when data races can occur in working with memory.
That property makes applications written against
WebGPU more likely to run without modification on different platforms.

<div class=example heading>
    Binding the same buffer for [=internal usage/storage=] as well as for
    [=internal usage/input=] within the same {{GPURenderPassEncoder}}
    results in a non-[=compatible usage list=] for that buffer.
</div>

<div class=example heading>
    These rules allow for <dfn dfn>read-only depth-stencil</dfn>: a single depth/stencil
    texture can be used as two different read-only usages in a render pass simultaneously:

    - [=internal usage/attachment-read=]

        As a depth/stencil attachment with all aspects marked read-only
        (using {{GPURenderPassDepthStencilAttachment/depthReadOnly}} and/or
        {{GPURenderPassDepthStencilAttachment/stencilReadOnly}} as necessary).

    - [=internal usage/constant=]

        As a texture binding to a draw call.
</div>

<div class=example heading>
    The <dfn dfn>usage scope storage exception</dfn> allows two cases that would
    not be allowed otherwise:

    - A buffer or texture may be bound as [=internal usage/storage=] to two
        different draw calls in a render pass.
    - Disjoint ranges of a single buffer may be bound to two different binding
        points as [=internal usage/storage=].

        Overlapping ranges may *not* be bound to a single dispatch/draw call;
        this is checked by "[$Encoder bind groups alias a writable resource$]".
</div>

<div class=example heading>
    The <dfn dfn>usage scope attachment exception</dfn> allows a texture subresource
    to be used as [=internal usage/attachment=] more than once.
    This is necessary to allow disjoint slices of 3D textures
    to be bound as different attachments to a single render pass.

    One slice may *not* be bound twice for two different attachments;
    this is checked by {{GPUCommandEncoder/beginRenderPass()}}.
</div>

### Synchronization ### {#programming-model-synchronization}

<!-- POSTV1(multi-queue): revise this section -->

A <dfn dfn>usage scope</dfn> is a [=map=] from [=subresource=] to [=list=]&lt;[=internal usage=]&gt;&gt;.
Each usage scope covers a range of operations which may execute in a concurrent
fashion with each other, and therefore may only use [=subresources=] in consistent
    [=compatible usage lists=] within the scope.

<div algorithm>
    A [=usage scope=] |scope| passes <dfn dfn>usage scope validation</dfn> if,
    for each [`subresource`, |usageList|] in |scope|,
    |usageList| is a [=compatible usage list=].
</div>

<div algorithm>
    To <dfn abstract-op for="usage scope" lt="add|Add">add</dfn>
    a [=subresource=] |subresource| to [=usage scope=] |usageScope| with usage
    ([=internal usage=] or set of [=internal usages=]) |usage|:

    1. If |usageScope|[|subresource|] does not [=map/exist=], set it to `[]`.
    1. [=list/Append=] |usage| to |usageScope|[|subresource|].
</div>

<div algorithm>
    To <dfn abstract-op for="usage scope" lt="merge|Merge">merge</dfn>
    [=usage scope=] |A| into [=usage scope=] |B|:

    1. For each [|subresource|, |usage|] in |A|:
        1. [$usage scope/Add$] |subresource| to |B| with usage |usage|.
</div>

[=Usage scopes=] are constructed and validated during encoding:
- in {{GPUComputePassEncoder/dispatchWorkgroups()}}
- in {{GPUComputePassEncoder/dispatchWorkgroupsIndirect()}}
- at {{GPURenderPassEncoder/end()|GPURenderPassEncoder.end()}}
- at {{GPURenderBundleEncoder/finish()|GPURenderBundleEncoder.finish()}}

The [=usage scopes=] are as follows:

- In a compute pass, each dispatch command ({{GPUComputePassEncoder/dispatchWorkgroups()}} or
    {{GPUComputePassEncoder/dispatchWorkgroupsIndirect()}}) is one usage scope.

    A subresource is used in the usage scope if it is
    potentially accessible by the dispatched invocations, including:

    - All [=subresources=] referenced by bind groups in slots used by the current
        {{GPUComputePipeline}}'s {{GPUPipelineBase/[[layout]]}}
    - Buffers used directly by dispatch calls (such as indirect buffers)

    Note:
    State-setting compute pass commands, like
    [=GPUBindingCommandsMixin/setBindGroup()=],
    do not contribute their bound resources directly to a usage scope: they only change the
    state that is checked in dispatch commands.
- One render pass is one usage scope.

    A subresource is used in the usage scope if it's referenced by any command,
    including state-setting commands (unlike in compute passes), including:

    - Buffers set by {{GPURenderCommandsMixin/setVertexBuffer()}}
    - Buffers set by {{GPURenderCommandsMixin/setIndexBuffer()}}
    - All [=subresources=] referenced by bind groups set by
        [=GPUBindingCommandsMixin/setBindGroup()=]
    - Buffers used directly by draw calls (such as indirect buffers)

Note: Copy commands are standalone operations and don't use [=usage scopes=] for validation.
They implement their own validation to prevent self-races.

<div class=example heading>
    The following example resource usages *are* included in [=usage scopes=]:

    - In a render pass, subresources used in any
        [=GPUBindingCommandsMixin/setBindGroup()=]
        call, regardless of whether the currently bound pipeline's
        shader or layout actually depends on these bindings,
        or the bind group is shadowed by another 'set' call.
    - A buffer used in any {{GPURenderCommandsMixin/setVertexBuffer()|setVertexBuffer()}}
        call, regardless of whether any draw call depends on this buffer,
        or whether this buffer is shadowed by another 'set' call.
    - A buffer used in any {{GPURenderCommandsMixin/setIndexBuffer()|setIndexBuffer()}}
        call, regardless of whether any draw call depends on this buffer,
        or whether this buffer is shadowed by another 'set' call.
    - A texture subresource used as a color attachment, resolve attachment, or
        depth/stencil attachment in {{GPURenderPassDescriptor}} by
        {{GPUCommandEncoder/beginRenderPass()|beginRenderPass()}},
        regardless of whether the shader actually depends on these attachments.
    - Resources used in bind group entries with visibility 0, or visible only
        to the compute stage but used in a render pass (or vice versa).
</div>

## Core Internal Objects ## {#core-internal-objects}

### Adapters ### {#adapters}

An <dfn dfn>adapter</dfn> identifies an implementation of WebGPU on the system:
both an instance of compute/rendering functionality on the
platform underlying a browser, and an instance of a browser's implementation of
WebGPU on top of that functionality.

[=Adapters=] are exposed via {{GPUAdapter}}.

[=Adapters=] do not uniquely represent underlying implementations:
calling {{GPU/requestAdapter()}} multiple times returns a different [=adapter=]
object each time.

Each [=adapter=] object can only be used to create one [=device=]:
upon a successful {{GPUAdapter/requestDevice()}} call, the adapter's {{adapter/[[state]]}}
changes to {{adapter/[[state]]/"consumed"}}.
Additionally, [=adapter=] objects may [$expire$] at any time.

Note:
This ensures applications use the latest system state for adapter selection when creating a device.
It also encourages robustness to more scenarios by making them look similar: first initialization,
reinitialization due to an unplugged adapter, reinitialization due to a test
{{GPUDevice/destroy()|GPUDevice.destroy()}} call, etc.

An [=adapter=] may be considered a <dfn>fallback adapter</dfn> if it has significant performance
caveats in exchange for some combination of wider compatibility, more predictable behavior, or
improved privacy. It is not required that a [=fallback adapter=] is available on every system.

[=adapter=] has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=adapter data-timeline=const>
    : <dfn>\[[features]]</dfn>, of type [=ordered set=]&lt;{{GPUFeatureName}}&gt;, readonly
    ::
        The [=features=] which can be used to create devices on this adapter.

    : <dfn>\[[limits]]</dfn>, of type [=supported limits=], readonly
    ::
        The [=limit/better|best=] limits which can be used to create devices on this adapter.

        Each adapter limit must be the same or [=limit/better=] than its default value
        in [=supported limits=].

    : <dfn>\[[fallback]]</dfn>, of type {{boolean}}, readonly
    ::
        If set to `true` indicates that the adapter is a [=fallback adapter=].

    : <dfn>\[[xrCompatible]]</dfn>, of type boolean
    ::
        If set to `true` indicates that the adapter was requested with compatibility with
        [=WebXR sessions=].
</dl>

[=adapter=] has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=adapter data-timeline=device>
    : <dfn>\[[state]]</dfn>, initially {{adapter/[[state]]/"valid"}}
    ::
        <dl dfn-type=enum-value dfn-for="adapter/[[state]]">
            : <dfn>"valid"</dfn>
            :: The adapter can be used to create a device.
            : <dfn>"consumed"</dfn>
            :: The adapter has already been used to create a device, and cannot be used again.
            : <dfn>"expired"</dfn>
            :: The adapter has expired for some other reason.
        </dl>
</dl>

<div algorithm data-timeline=device>
    To <dfn abstract-op lt='expire|Expire|expires'>expire</dfn> a {{GPUAdapter}} |adapter|, run the
    following [=device timeline=] steps:

    1. Set |adapter|.{{GPUAdapter/[[adapter]]}}.{{adapter/[[state]]}} to
        {{adapter/[[state]]/"expired"}}.
</div>

### Devices ### {#devices}

A <dfn dfn>device</dfn> is the logical instantiation of an [=adapter=],
through which [=internal objects=] are created.

<!-- POSTV1(multithreading) tentative text:
It can be shared across multiple [=agents=] (e.g. dedicated workers).
-->

[=Devices=] are exposed via {{GPUDevice}}.

A [=device=] is the exclusive owner of all [=internal objects=] created from it:
when the [=device=] becomes [$invalid$]
(is [=lose the device|lost=] or {{GPUDevice/destroy()|destroyed}}),
it and all objects created on it (directly, e.g.
{{GPUDevice/createTexture()}}, or indirectly, e.g. {{GPUTexture/createView()}}) become
implicitly [$valid to use with|unusable$].

[=device=] has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=device data-timeline=const>
    : <dfn>\[[adapter]]</dfn>, of type [=adapter=], readonly
    ::
        The [=adapter=] from which this device was created.

    : <dfn>\[[features]]</dfn>, of type [=ordered set=]&lt;{{GPUFeatureName}}&gt;, readonly
    ::
        The [=features=] which can be used on this device, as computed [=a new device|at creation=].
        No additional features can be used, even if the underlying [=adapter=] can support them.

    : <dfn>\[[limits]]</dfn>, of type [=supported limits=], readonly
    ::
        The limits which can be used on this device, as computed [=a new device|at creation=].
        No [=limit/better=] limits can be used, even if the underlying [=adapter=] can support them.
</dl>

[=device=] has the following [=content timeline properties=]:

<dl dfn-type=attribute dfn-for=device data-timeline=content>
    : <dfn>[[content device]]</dfn>, of type {{GPUDevice}}, readonly
    ::
        The [=Content timeline=] {{GPUDevice}} interface which this device is associated with.
</dl>

<div algorithm data-timeline=device>
    To create <dfn dfn>a new device</dfn> from [=adapter=] |adapter|
    with {{GPUDeviceDescriptor}} |descriptor|, run the following [=device timeline=] steps:

    1. Let |features| be the [=set=] of values in
        |descriptor|.{{GPUDeviceDescriptor/requiredFeatures}}.
    1. If |features| contains {{GPUFeatureName/"texture-formats-tier2"}}:
        1. [=set/Append=] {{GPUFeatureName/"texture-formats-tier1"}} to |features|
    1. If |features| contains {{GPUFeatureName/"texture-formats-tier1"}}:
        1. [=set/Append=] {{GPUFeatureName/"rg11b10ufloat-renderable"}} to |features|
    1. [=set/Append=] {{GPUFeatureName/"core-features-and-limits"}} to |features|.
    1. Let |limits| be a [=supported limits=] object with all values set to their defaults.
    1. For each (|key|, |value|) pair in |descriptor|.{{GPUDeviceDescriptor/requiredLimits}}:
        1. If |value| is not `undefined` and |value| is [=limit/better=] than |limits|[|key|]:
            1. Set |limits|[|key|] to |value|.
    1. Let |device| be a [=device=] object.
    1. Set |device|.{{device/[[adapter]]}} to |adapter|.
    1. Set |device|.{{device/[[features]]}} to |features|.
    1. Set |device|.{{device/[[limits]]}} to |limits|.
    1. Return |device|.
</div>

Any time the user agent needs to revoke access to a device, it calls
[=lose the device=](`device`, {{GPUDeviceLostReason/"unknown"}}) on the device's [=device timeline=],
potentially ahead of other operations currently queued on that timeline.

If an operation fails with side effects that would observably change the state
of objects on the device or potentially corrupt internal implementation/driver state,
the device **should** be lost to prevent these changes from being observable.

Note:
For all device losses not initiated by the application (via {{GPUDevice/destroy()}}),
user agents should consider issuing developer-visible warnings *unconditionally*,
even if the {{GPUDevice/lost}} promise is handled.
These scenarios should be rare, and the signal is vital to developers because most of the WebGPU
API tries to behave like nothing is wrong to avoid interrupting the runtime flow of the application:
no validation errors are raised, most promises resolve normally, etc.

<div algorithm="lose the device" data-timeline=device>
    To <dfn dfn>lose the device</dfn>(|device|, |reason|) run the following [=device timeline=] steps:

    1. [$Invalidate$] |device|.
    1. Issue the following steps on the [=content timeline=] of |device|.{{device/[[content device]]}}:
        <div data-timeline=content>
            1. Resolve |device|.{{GPUDevice/lost}} with a new {{GPUDeviceLostInfo}} with
                {{GPUDeviceLostInfo/reason}} set to |reason| and
                {{GPUDeviceLostInfo/message}} set to an [=implementation-defined=] value.

                Note: {{GPUDeviceLostInfo/message}} should not disclose unnecessary user/system
                information and should never be parsed by applications.
        </div>

    1. Complete any outstanding steps that are waiting until |device| <dfn dfn>becomes lost</dfn>.

    Note: No errors are generated from a device which is lost.
    See [[#errors-and-debugging]].
</div>

<div algorithm data-timeline=device>
    To <dfn abstract-op lt="Listen for timeline event">listen for timeline event</dfn>
    |event| on [=device=] |device|, handled by |steps| on timeline |timeline|:

    - If or when the [=device timeline=] has been informed of the completion of |event|, or
    - If |device| is [$invalid|lost$] already, or when it [=becomes lost=]:

    Then issue |steps| on |timeline|.
</div>

## Optional Capabilities ## {#optional-capabilities}

WebGPU [=adapters=] and [=devices=] have <dfn dfn>capabilities</dfn>, which
describe WebGPU functionality that differs between different implementations,
typically due to hardware or system software constraints.
A [=capability=] is either a [=feature=] or a [=limit=].

A user agent must not reveal more than 32 distinguishable configurations or buckets.

The capabilities of an [=adapter=] must conform to [[#adapter-capability-guarantees]].

Only supported capabilities may be requested in {{GPUAdapter/requestDevice()}};
requesting unsupported capabilities results in failure.

The capabilities of a [=device=] are determined in "[=a new device=]" by starting with the adapter's
defaults (no features and the default [=supported limits=])
and adding capabilities as requested in {{GPUAdapter/requestDevice()}}.
These capabilities are enforced regardless of the capabilities of the [=adapter=].

<p tracking-vector>
For privacy considerations, see [[#privacy-machine-limits]].

### Features ### {#features}

A <dfn dfn>feature</dfn> is a set of optional WebGPU functionality that is not supported
on all implementations, typically due to hardware or system software constraints.

All [=features=] are optional, but [=adapters=] make some guarantees about their availability
(see [[#adapter-capability-guarantees]]).

A [=device=] supports the exact set of features determined at creation (see [[#optional-capabilities]]).
API calls perform validation according to these features (not the [=adapter=]'s features):

- Using existing API surfaces in a new way **typically** results in a [$validation error$].
- There are several types of <dfn dfn>optional API surface</dfn>:
    - Using a new method or enum value always throws a {{TypeError}}.
    - Using a new dictionary member with a (correctly-typed) non-default value **typically**
        results in a [$validation error$].
    - Using a new WGSL `enable` directive always results in a {{GPUDevice/createShaderModule()}}
        [$validation error$].

<div algorithm data-timeline=const>
    A {{GPUFeatureName}} |feature| is <dfn dfn>enabled for</dfn>
    a {{GPUObjectBase}} |object| if and only if
    |object|.{{GPUObjectBase/[[device]]}}.{{device/[[features]]}} [=list/contains=] |feature|.
</div>

See the [[#feature-index|Feature Index]] for a description of the functionality each feature enables.

Note:
Enabling features may not necessarily be desirable, as doing so may have a performance impact.
Because of this, and to improve portability across devices and implementations,
applications should generally only request features that they may actually require.

### Limits ### {#limits}

Each <dfn dfn>limit</dfn> is a numeric limit on the usage of WebGPU on a device.

Note:
Setting "better" limits may not necessarily be desirable, as doing so may have a performance impact.
Because of this, and to improve portability across devices and implementations, applications should
generally only request limits better than the defaults if they may actually require them.

Each limit has a <dfn dfn for=limit>default</dfn> value.

[=Adapters=] are always guaranteed to support the defaults or [=limit/better=]
(see [[#adapter-capability-guarantees]]).

A [=device=] supports the exact set of limits determined at creation (see [[#optional-capabilities]]).
API calls perform validation according to these limits (not the [=adapter=]'s limits),
no [=limit/better=] or worse.

For any given limit, some values are <dfn dfn for=limit>better</dfn> than others.
A [=limit/better=] limit value always relaxes validation, enabling strictly
more programs to be valid. For each [=limit class=], "better" is defined.

Different limits have different <dfn dfn lt="limit class">limit classes</dfn>:

<dl dfn-type=dfn dfn-for="limit class">
    : <dfn>maximum</dfn>
    ::
        The limit enforces a maximum on some value passed into the API.

        Higher values are [=limit/better=].

        May only be set to values &ge; the [=limit/default=].
        Lower values are clamped to the [=limit/default=].

    : <dfn>alignment</dfn>
    ::
        The limit enforces a minimum alignment on some value passed into the API; that is,
        the value must be a multiple of the limit.

        Lower values are [=limit/better=].

        May only be set to powers of 2 which are &le; the [=limit/default=].
        Values which are not powers of 2 are invalid.
        Higher powers of 2 are clamped to the [=limit/default=].
</dl>

A <dfn dfn>supported limits</dfn> object has a value for every limit defined by WebGPU:

<table class=data dfn-type=attribute dfn-for="supported limits">
    <thead>
        <tr><th>Limit name <th>Type <th>[=Limit class=] <th>[=limit/Default=]
    </thead>

    <tr><td><dfn>maxTextureDimension1D</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8192
    <tr class=row-continuation><td colspan=4>
        The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=]
        of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"1d"}}.

    <tr><td><dfn>maxTextureDimension2D</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8192
    <tr class=row-continuation><td colspan=4>
        The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] and {{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=]
        of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"2d"}}.

    <tr><td><dfn>maxTextureDimension3D</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>2048
    <tr class=row-continuation><td colspan=4>
        The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=], {{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] and {{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=]
        of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"3d"}}.

    <tr><td><dfn>maxTextureArrayLayers</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>256
    <tr class=row-continuation><td colspan=4>
        The maximum allowed value for the {{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=]
        of a [=texture=] created with {{GPUTextureDescriptor/dimension}} {{GPUTextureDimension/"2d"}}.

    <tr><td><dfn>maxBindGroups</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>4
    <tr class=row-continuation><td colspan=4>
        The maximum number of {{GPUBindGroupLayout|GPUBindGroupLayouts}}
        allowed in {{GPUPipelineLayoutDescriptor/bindGroupLayouts}}
        when creating a {{GPUPipelineLayout}}.

    <tr><td><dfn>maxBindGroupsPlusVertexBuffers</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>24
    <tr class=row-continuation><td colspan=4>
        The maximum number of bind group and vertex buffer slots used simultaneously,
        counting any empty slots below the highest index.
        Validated in {{GPUDevice/createRenderPipeline()}} and [$valid to draw|in draw calls$].

    <tr><td><dfn>maxBindingsPerBindGroup</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>1000
    <tr class=row-continuation><td colspan=4>
        The number of binding indices available when creating a {{GPUBindGroupLayout}}.

        Note: This limit is normative, but arbitrary.
        With the default [=exceeds the binding slot limits|binding slot limits=], it is impossible
        to use 1000 bindings in one bind group, but this allows
        {{GPUBindGroupLayoutEntry}}.{{GPUBindGroupLayoutEntry/binding}} values up to 999.
        This limit allows implementations to treat binding space as an array,
        within reasonable memory space, rather than a sparse map structure.

    <tr><td><dfn>maxDynamicUniformBuffersPerPipelineLayout</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8
    <tr class=row-continuation><td colspan=4>
        The maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are uniform buffers with dynamic offsets.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxDynamicStorageBuffersPerPipelineLayout</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>4
    <tr class=row-continuation><td colspan=4>
        The maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are storage buffers with dynamic offsets.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxSampledTexturesPerShaderStage</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>16
    <tr class=row-continuation><td colspan=4>
        For each possible {{GPUShaderStage}} `stage`,
        the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are sampled textures.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxSamplersPerShaderStage</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>16
    <tr class=row-continuation><td colspan=4>
        For each possible {{GPUShaderStage}} `stage`,
        the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are samplers.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxStorageBuffersPerShaderStage</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8
    <tr class=row-continuation><td colspan=4>
        For each possible {{GPUShaderStage}} `stage`,
        the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are storage buffers.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxStorageTexturesPerShaderStage</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>4
    <tr class=row-continuation><td colspan=4>
        For each possible {{GPUShaderStage}} `stage`,
        the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are storage textures.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxUniformBuffersPerShaderStage</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>12
    <tr class=row-continuation><td colspan=4>
        For each possible {{GPUShaderStage}} `stage`,
        the maximum number of {{GPUBindGroupLayoutEntry}} entries across a {{GPUPipelineLayout}}
        which are uniform buffers.
        See [=Exceeds the binding slot limits=].

    <tr><td><dfn>maxUniformBufferBindingSize</dfn>
        <td>{{GPUSize64}} <td>[=limit class/maximum=] <td>65536 bytes
    <tr class=row-continuation><td colspan=4>
        The maximum {{GPUBufferBinding}}.{{GPUBufferBinding/size}} for bindings with a
        {{GPUBindGroupLayoutEntry}} |entry| for which
        |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
        is {{GPUBufferBindingType/"uniform"}}.

    <tr><td><dfn>maxStorageBufferBindingSize</dfn>
        <td>{{GPUSize64}} <td>[=limit class/maximum=] <td>134217728 bytes (128 MiB)
    <tr class=row-continuation><td colspan=4>
        The maximum {{GPUBufferBinding}}.{{GPUBufferBinding/size}} for bindings with a
        {{GPUBindGroupLayoutEntry}} |entry| for which
        |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
        is {{GPUBufferBindingType/"storage"}}
        or {{GPUBufferBindingType/"read-only-storage"}}.

    <tr><td><dfn>minUniformBufferOffsetAlignment</dfn>
        <td>{{GPUSize32}} <td>[=limit class/alignment=] <td>256 bytes
    <tr class=row-continuation><td colspan=4>
        The required alignment for {{GPUBufferBinding}}.{{GPUBufferBinding/offset}} and
        the dynamic offsets provided in [=GPUBindingCommandsMixin/setBindGroup()=],
        for bindings with a {{GPUBindGroupLayoutEntry}} |entry| for which
        |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
        is {{GPUBufferBindingType/"uniform"}}.

    <tr><td><dfn>minStorageBufferOffsetAlignment</dfn>
        <td>{{GPUSize32}} <td>[=limit class/alignment=] <td>256 bytes
    <tr class=row-continuation><td colspan=4>
        The required alignment for {{GPUBufferBinding}}.{{GPUBufferBinding/offset}} and
        the dynamic offsets provided in [=GPUBindingCommandsMixin/setBindGroup()=],
        for bindings with a {{GPUBindGroupLayoutEntry}} |entry| for which
        |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
        is {{GPUBufferBindingType/"storage"}}
        or {{GPUBufferBindingType/"read-only-storage"}}.

    <tr><td><dfn>maxVertexBuffers</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8
    <tr class=row-continuation><td colspan=4>
        The maximum number of {{GPUVertexState/buffers}}
        when creating a {{GPURenderPipeline}}.

    <tr><td><dfn>maxBufferSize</dfn>
        <td>{{GPUSize64}} <td>[=limit class/maximum=] <td>268435456 bytes (256 MiB)
    <tr class=row-continuation><td colspan=4>
        The maximum size of {{GPUBufferDescriptor/size}}
        when creating a {{GPUBuffer}}.

    <tr><td><dfn>maxVertexAttributes</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>16
    <tr class=row-continuation><td colspan=4>
        The maximum number of {{GPUVertexBufferLayout/attributes}}
        in total across {{GPUVertexState/buffers}}
        when creating a {{GPURenderPipeline}}.

    <tr><td><dfn>maxVertexBufferArrayStride</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>2048 bytes
    <tr class=row-continuation><td colspan=4>
        The maximum allowed {{GPUVertexBufferLayout/arrayStride}}
        when creating a {{GPURenderPipeline}}.

    <tr><td><dfn>maxInterStageShaderVariables</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>16
    <tr class=row-continuation><td colspan=4>
        The maximum allowed number of input or output variables for inter-stage
        communication (like vertex outputs or fragment inputs).

    <tr><td><dfn>maxColorAttachments</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>8
    <tr class=row-continuation><td colspan=4>
        The maximum allowed number of color attachments in
        {{GPURenderPipelineDescriptor}}.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}},
        {{GPURenderPassDescriptor}}.{{GPURenderPassDescriptor/colorAttachments}},
        and {{GPURenderPassLayout}}.{{GPURenderPassLayout/colorFormats}}.

    <tr><td><dfn>maxColorAttachmentBytesPerSample</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>32
    <tr class=row-continuation><td colspan=4>
        The maximum number of bytes necessary to hold one sample (pixel or subpixel)
        of render pipeline output data, across all color attachments.

    <tr><td><dfn>maxComputeWorkgroupStorageSize</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>16384 bytes
    <tr class=row-continuation><td colspan=4>
        The maximum number of bytes of [=address spaces/workgroup=] storage used for a compute stage
        {{GPUShaderModule}} entry-point.

    <tr><td><dfn>maxComputeInvocationsPerWorkgroup</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>256
    <tr class=row-continuation><td colspan=4>
        The maximum value of the product of the `workgroup_size` dimensions for a
        compute stage {{GPUShaderModule}} entry-point.

    <tr><td><dfn>maxComputeWorkgroupSizeX</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>256
    <tr class=row-continuation><td colspan=4>
        The maximum value of the `workgroup_size` X dimension for a
        compute stage {{GPUShaderModule}} entry-point.

    <tr><td><dfn>maxComputeWorkgroupSizeY</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>256
    <tr class=row-continuation><td colspan=4>
        The maximum value of the `workgroup_size` Y dimensions for a
        compute stage {{GPUShaderModule}} entry-point.

    <tr><td><dfn>maxComputeWorkgroupSizeZ</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>64
    <tr class=row-continuation><td colspan=4>
        The maximum value of the `workgroup_size` Z dimensions for a
        compute stage {{GPUShaderModule}} entry-point.

    <tr><td><dfn>maxComputeWorkgroupsPerDimension</dfn>
        <td>{{GPUSize32}} <td>[=limit class/maximum=] <td>65535
    <tr class=row-continuation><td colspan=4>
        The maximum value for the arguments of
        {{GPUComputePassEncoder/dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ)}}.
</table>

<h5 id=gpusupportedlimits data-dfn-type=interface>`GPUSupportedLimits`
<span id=gpu-supportedlimits></span>
</h5>

{{GPUSupportedLimits}} exposes an adapter or device's [=supported limits=].
See {{GPUAdapter/limits|GPUAdapter.limits}} and {{GPUDevice/limits|GPUDevice.limits}}.

<!-- When adding limits here, add them to the Correspondence Reference as well. -->

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUSupportedLimits {
    readonly attribute unsigned long maxTextureDimension1D;
    readonly attribute unsigned long maxTextureDimension2D;
    readonly attribute unsigned long maxTextureDimension3D;
    readonly attribute unsigned long maxTextureArrayLayers;
    readonly attribute unsigned long maxBindGroups;
    readonly attribute unsigned long maxBindGroupsPlusVertexBuffers;
    readonly attribute unsigned long maxBindingsPerBindGroup;
    readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
    readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
    readonly attribute unsigned long maxSampledTexturesPerShaderStage;
    readonly attribute unsigned long maxSamplersPerShaderStage;
    readonly attribute unsigned long maxStorageBuffersPerShaderStage;
    readonly attribute unsigned long maxStorageTexturesPerShaderStage;
    readonly attribute unsigned long maxUniformBuffersPerShaderStage;
    readonly attribute unsigned long long maxUniformBufferBindingSize;
    readonly attribute unsigned long long maxStorageBufferBindingSize;
    readonly attribute unsigned long minUniformBufferOffsetAlignment;
    readonly attribute unsigned long minStorageBufferOffsetAlignment;
    readonly attribute unsigned long maxVertexBuffers;
    readonly attribute unsigned long long maxBufferSize;
    readonly attribute unsigned long maxVertexAttributes;
    readonly attribute unsigned long maxVertexBufferArrayStride;
    readonly attribute unsigned long maxInterStageShaderVariables;
    readonly attribute unsigned long maxColorAttachments;
    readonly attribute unsigned long maxColorAttachmentBytesPerSample;
    readonly attribute unsigned long maxComputeWorkgroupStorageSize;
    readonly attribute unsigned long maxComputeInvocationsPerWorkgroup;
    readonly attribute unsigned long maxComputeWorkgroupSizeX;
    readonly attribute unsigned long maxComputeWorkgroupSizeY;
    readonly attribute unsigned long maxComputeWorkgroupSizeZ;
    readonly attribute unsigned long maxComputeWorkgroupsPerDimension;
};
</script>

<h5 id=gpusupportedfeatures data-dfn-type=interface>`GPUSupportedFeatures`
<span id=gpu-supportedfeatures></span>
</h5>

{{GPUSupportedFeatures}} is a [=setlike=] interface. Its [=set entries=] are
the {{GPUFeatureName}} values of the [=features=] supported by an adapter or
device. It must only contain strings from the {{GPUFeatureName}} enum.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUSupportedFeatures {
    readonly setlike<DOMString>;
};
</script>

<div class=note heading>
    The type of the {{GPUSupportedFeatures}} [=set entries=] is {{DOMString}} to allow user
    agents to gracefully handle valid {{GPUFeatureName}}s which are added in later revisions of the spec
    but which the user agent has not been updated to recognize yet. If the [=set entries=] type was
    {{GPUFeatureName}} the following code would throw an {{TypeError}} rather than reporting `false`:

    <div class=example>
        Check for support of an unrecognized feature:

        <pre highlight=js>
            if (adapter.features.has('unknown-feature')) {
                // Use unknown-feature
            } else {
                console.warn('unknown-feature is not supported by this adapter.');
            }
        </pre>
    </div>
</div>

<h5 id=gpuwgsllanguagefeatures data-dfn-type=interface>`WGSLLanguageFeatures`
</h5>

{{WGSLLanguageFeatures}} is the [=setlike=] interface of
<code>navigator.gpu.{{GPU/wgslLanguageFeatures}}</code>.
Its [=set entries=] are the string names of the WGSL [=language extensions=]
supported by the implementation (regardless of the adapter or device).

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface WGSLLanguageFeatures {
    readonly setlike<DOMString>;
};
</script>

<h5 id=gpuadapterinfo data-dfn-type=interface>`GPUAdapterInfo`
<span id=gpu-adapterinfo></span>
</h5>

{{GPUAdapterInfo}} exposes various identifying information about an adapter.

None of the members in {{GPUAdapterInfo}} are guaranteed to be populated with any particular value;
if no value is provided, the attribute will return the empty string `""`. It is at the user
agent's discretion which values to reveal, and it is likely that on some devices none of the values
will be populated. As such, applications **must** be able to handle any possible {{GPUAdapterInfo}} values,
including the absence of those values.

The {{GPUAdapterInfo}} for an adapter is exposed via {{GPUAdapter/info|GPUAdapter.info}}
and {{GPUDevice/adapterInfo|GPUDevice.adapterInfo}}).
This info is immutable:
for a given adapter, each {{GPUAdapterInfo}} attribute will return the same value every time it's accessed.

Note:
Though the {{GPUAdapterInfo}} attributes are immutable *once accessed*, an implementation may delay the decision on
what to expose for each attribute until the first time it is accessed.

Note:
Other {{GPUAdapter}} instances, even if they represent the same physical adapter, may expose
different values in {{GPUAdapterInfo}}.
However, they **should** expose the same values unless a specific
event has increased the amount of identifying information the page is allowed to access.
(No such events are defined by this specification.)

<p tracking-vector>
For privacy considerations, see [[#privacy-adapter-identifiers]].
</p>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUAdapterInfo {
    readonly attribute DOMString vendor;
    readonly attribute DOMString architecture;
    readonly attribute DOMString device;
    readonly attribute DOMString description;
    readonly attribute unsigned long subgroupMinSize;
    readonly attribute unsigned long subgroupMaxSize;
    readonly attribute boolean isFallbackAdapter;
};
</script>

{{GPUAdapterInfo}} has the following attributes:

<dl dfn-type=attribute dfn-for=GPUAdapterInfo data-timeline=content>
    : <dfn>vendor</dfn>
    ::
        The name of the vendor of the [=adapter=], if available. Empty string otherwise.

    : <dfn>architecture</dfn>
    ::
        The name of the family or class of GPUs the [=adapter=] belongs to, if available. Empty
        string otherwise.

    : <dfn>device</dfn>
    ::
        A vendor-specific identifier for the [=adapter=], if available. Empty string otherwise.

        Note: This is a value that represents the type of adapter. For example, it may be a
        [PCI device ID](https://pcisig.com/). It does not uniquely identify a given piece of
        hardware like a serial number.

    : <dfn>description</dfn>
    ::
        A human readable string describing the [=adapter=] as reported by the driver, if available.
        Empty string otherwise.

        Note: Because no formatting is applied to {{GPUAdapterInfo/description}} attempting to parse
        this value is not recommended. Applications which change their behavior based on the
        {{GPUAdapterInfo}}, such as applying workarounds for known driver issues, should rely on the
        other fields when possible.

    : <dfn>subgroupMinSize</dfn>
    ::
        If the {{GPUFeatureName/"subgroups"}} feature is supported, the minimum
        supported [=builtin-value/subgroup size=] for the [=adapter=].

    : <dfn>subgroupMaxSize</dfn>
    ::
        If the {{GPUFeatureName/"subgroups"}} feature is supported, the maximum
        supported [=builtin-value/subgroup size=] for the [=adapter=].

    : <dfn>isFallbackAdapter</dfn>
    ::
        Whether the [=adapter=] is a [=fallback adapter=].
</dl>

<div algorithm data-timeline=content>
    To create a <dfn abstract-op>new adapter info</dfn> for a given [=adapter=] |adapter|, run the
    following [=content timeline=] steps:

    1. Let |adapterInfo| be a new {{GPUAdapterInfo}}.

    1. If the vendor is known, set |adapterInfo|.{{GPUAdapterInfo/vendor}} to the name of
        |adapter|'s vendor as a [=normalized identifier string=]. To preserve privacy, the user
        agent may instead set |adapterInfo|.{{GPUAdapterInfo/vendor}} to the empty string or a
        reasonable approximation of the vendor as a [=normalized identifier string=].

    1. If |the architecture is known, set |adapterInfo|.{{GPUAdapterInfo/architecture}} to a
        [=normalized identifier string=] representing the family or class of adapters to which
        |adapter| belongs. To preserve privacy, the user agent may instead set
        |adapterInfo|.{{GPUAdapterInfo/architecture}} to the empty string or a reasonable
        approximation of the architecture as a [=normalized identifier string=].

    1. If the device is known, set |adapterInfo|.{{GPUAdapterInfo/device}} to a
        [=normalized identifier string=] representing a vendor-specific identifier for |adapter|.
        To preserve privacy, the user agent may instead set |adapterInfo|.{{GPUAdapterInfo/device}}
        to to the empty string or a reasonable approximation of a vendor-specific identifier as a
        [=normalized identifier string=].

    1. If a description is known, set |adapterInfo|.{{GPUAdapterInfo/description}} to a description
        of the |adapter| as reported by the driver. To preserve privacy, the user agent may
        instead set |adapterInfo|.{{GPUAdapterInfo/description}} to the empty string or a
        reasonable approximation of a description.

    1. If {{GPUFeatureName/"subgroups"}} is supported, set {{GPUAdapterInfo/subgroupMinSize}}
        to the smallest supported subgroup size. Otherwise, set this value to 4.

        Note: To preserve privacy, the user agent may choose to not support some features or provide values
        for the property which do not distinguish different devices, but are still usable
        (e.g. use the default value of 4 for all devices).

    1. If {{GPUFeatureName/"subgroups"}} is supported, set {{GPUAdapterInfo/subgroupMaxSize}}
        to the largest supported subgroup size. Otherwise, set this value to 128.

        Note: To preserve privacy, the user agent may choose to not support some features or provide values
        for the property which do not distinguish different devices, but are still usable
        (e.g. use the default value of 128 for all devices).

    1. Set |adapterInfo|.{{GPUAdapterInfo/isFallbackAdapter}} to |adapter|.{{adapter/[[fallback]]}}.

    1. Return |adapterInfo|.
</div>

<div algorithm data-timeline=content>
    A <dfn>normalized identifier string</dfn> is one that follows the following pattern:

    `[a-z0-9]+(-[a-z0-9]+)*`
    <pre class=railroad>
        OneOrMore:
            OneOrMore:
                T: a-z 0-9
            T: -
    </pre>

    <div class=example>
        Examples of valid normalized identifier strings include:

        - `gpu`
        - `3d`
        - `0x3b2f`
        - `next-gen`
        - `series-x20-ultra`
    </div>
</div>

## Extension Documents ## {#extension-documents}

"Extension Documents" are additional documents which describe new functionality which is
non-normative and **not part of the WebGPU/WGSL specifications**.
They describe functionality that builds upon these specifications, often including one or more new
API [=feature=] flags and/or WGSL `enable` directives, or interactions with other draft
web specifications.

WebGPU implementations **must not** expose extension functionality; doing so is a spec violation.
New functionality does not become part of the WebGPU standard until it is integrated
into the WebGPU specification (this document) and/or WGSL specification.

## Origin Restrictions ## {#origin-restrictions}

WebGPU allows accessing image data stored in images, videos, and canvases.
Restrictions are imposed on the use of cross-domain media, because shaders can be used to
indirectly deduce the contents of textures which have been uploaded to the GPU.

WebGPU disallows uploading an image source if it <l spec=html>[=is not origin-clean=]</l>.

This also implies that the [=origin-clean=] flag for a
canvas rendered using WebGPU will never be set to `false`.

For more information on issuing CORS requests for image and video elements, consult:

- [[html#cors-settings-attributes]]
- [[html#the-img-element]] <{img}>
- [[html#media-elements]] {{HTMLMediaElement}}

## Task Sources ## {#task-sources}

### WebGPU Task Source ### {#-webgpu-task-source}

WebGPU defines a new [=task source=] called the <dfn dfn>WebGPU task source</dfn>.
It is used for the {{GPUDevice/uncapturederror}} event and {{GPUDevice}}.{{GPUDevice/lost}}.

<div algorithm data-timeline=content>
    To <dfn abstract-op>queue a global task for {{GPUDevice}}</dfn> |device|,
    with a series of steps |steps| on the [=content timeline=]:

    1. [=Queue a global task=] on the [=WebGPU task source=], with the global object that was used
        to create |device|, and the steps |steps|.
</div>

<h4 id=automatic-expiry-task-source data-dfn-type=dfn>Automatic Expiry Task Source
</h4>

WebGPU defines a new [=task source=] called the [=automatic expiry task source=].
It is used for the automatic, timed expiry (destruction) of certain objects:

- {{GPUTexture}}s returned by {{GPUCanvasContext/getCurrentTexture()}}
- {{GPUExternalTexture}}s created from {{HTMLVideoElement}}s

<div algorithm data-timeline=content>
    To <dfn abstract-op>queue an automatic expiry task</dfn>
    with {{GPUDevice}} |device| and a series of steps |steps| on the [=content timeline=]:

    1. [=Queue a global task=] on the [=automatic expiry task source=], with the global object that
        was used to create |device|, and the steps |steps|.
</div>

Tasks from the [=automatic expiry task source=] **should** be processed with high priority; in
particular, once queued, they **should** run before user-defined (JavaScript) tasks.

<div class=note heading>
    This behavior is more predictable, and the strictness helps developers write more portable
    applications by eagerly detecting incorrect assumptions about implicit lifetimes that may be
    hard to detect. Developers are still strongly encouraged to test in multiple implementations.

    Implementation note:
    It is valid to implement a high-priority expiry "task" by instead inserting additional steps at
    a fixed point inside the [=event loop processing model=] rather than running an actual task.
</div>

## Color Spaces and Encoding ## {#color-spaces}

WebGPU does not provide color management. All values within WebGPU (such as texture elements)
are raw numeric values, not color-managed color values.

WebGPU *does* interface with color-managed outputs (via {{GPUCanvasConfiguration}}) and inputs
(via {{GPUQueue/copyExternalImageToTexture()}} and {{GPUDevice/importExternalTexture()}}).
Thus, color conversion must be performed between the WebGPU numeric values and the external color values.
Each such interface point locally defines an encoding (color space, transfer function, and alpha
premultiplication) in which the WebGPU numeric values are to be interpreted.

WebGPU allows all of the color spaces in the {{PredefinedColorSpace}} enum.
Note, each color space is defined over an extended range, as defined by the referenced CSS definitions,
to represent color values outside of its space (in both chrominance and luminance).

An <dfn dfn>out-of-gamut premultiplied RGBA value</dfn> is one where any of the R/G/B channel values
exceeds the alpha channel value. For example, the premultiplied sRGB RGBA value [1.0, 0, 0, 0.5]
represents the (unpremultiplied) color [2, 0, 0] with 50% alpha, written `rgb(srgb 2 0 0 / 50%)` in CSS.
Just like any color value outside the sRGB color gamut, this is a well defined point in the extended color space
(except when alpha is 0, in which case there is no color).
However, when such values are output to a visible canvas, the result is undefined
(see {{GPUCanvasAlphaMode}} {{GPUCanvasAlphaMode/"premultiplied"}}).

### Color Space Conversions ### {#color-space-conversions}

A color is converted between spaces by translating its representation in one space to a
representation in another according to the definitions above.

If the source value has fewer than 4 RGBA channels, the missing green/blue/alpha channels are set to
`0, 0, 1`, respectively, before converting for color space/encoding and alpha premultiplication.
After conversion, if the destination needs fewer than 4 channels, the additional channels
are ignored.

Note:
Grayscale images generally represent RGB values `(V, V, V)`, or RGBA values `(V, V, V, A)` in their color space.

Colors are not lossily clamped during conversion: converting from one color space to another
will result in values outside the range [0, 1] if the source color values were outside the range
of the destination color space's gamut. For an sRGB destination, for example, this can occur if the
source is rgba16float, in a wider color space like Display-P3, or is premultiplied and contains
[=out-of-gamut premultiplied RGBA value|out-of-gamut values=].

Similarly, if the source value has a high bit depth (e.g. PNG with 16 bits per component) or
extended range (e.g. canvas with `float16` storage), these colors are preserved through color space
conversion, with intermediate computations having at least the precision of the source.

### Color Space Conversion Elision ### {#color-space-conversion-elision}

If the source and destination of a color space/encoding conversion are the same, then conversion
is not necessary. In general, if any given step of the conversion is an identity function (no-op),
implementations **should** elide it, for performance.

For optimal performance, applications **should** set their color space and encoding
options so that the number of necessary conversions is minimized throughout the process.
For various image sources of {{GPUCopyExternalImageSourceInfo}}:

- {{ImageBitmap}}:
    - Premultiplication is controlled via {{ImageBitmapOptions/premultiplyAlpha}}.
    - Color space is controlled via {{ImageBitmapOptions/colorSpaceConversion}}.
- 2d canvas:
    - [[html#premultiplied-alpha-and-the-2d-rendering-context|Always premultiplied]].
    - Color space is controlled via the {{CanvasRenderingContext2DSettings/colorSpace}} context creation attribute.
- WebGL canvas:
    - Premultiplication is controlled via the `premultipliedAlpha` option in {{WebGLContextAttributes}}.
    - Color space is controlled via the {{WebGLRenderingContextBase}}'s {{WebGLRenderingContextBase/drawingBufferColorSpace}} state.

Note: Check browser implementation support for these features before relying on them.

## Numeric conversions from JavaScript to WGSL ## {#conversions-to-wgsl}

Several parts of the WebGPU API ([=pipeline-overridable=] {{GPUProgrammableStage/constants}} and
render pass clear values) take numeric values from WebIDL ({{double}} or {{float}}) and convert
them to WGSL values (`bool`, `i32`, `u32`, `f32`, `f16`).

<div algorithm data-timeline=device>
    To convert an IDL value |idlValue| of type {{double}} or {{float}} <dfn abstract-op>to WGSL type</dfn> |T|,
    possibly throwing a {{TypeError}}, run the following [=device timeline=] steps:

    Note: This {{TypeError}} is generated in the [=device timeline=] and never surfaced to JavaScript.

    1. [=Assert=] |idlValue| is a finite value, since it is not {{unrestricted double}} or {{unrestricted float}}.

    1. Let |v| be the ECMAScript Number resulting from [=!=] converting |idlValue| to
        [=converted to an ECMAScript value|an ECMAScript value=].
        <!-- This back-conversion is just here so we can call back into the ES->IDL conversion definitions from WebIDL. -->

    1. <dl class=switch>
            : If |T| is `bool`
            ::
                Return the WGSL `bool` value corresponding to the result of [=!=] converting |v| to
                [=converted to an IDL value|an IDL value=] of type {{boolean}}.

                Note:
                This algorithm is called after the conversion from an ECMAScript value to an IDL
                {{double}} or {{float}} value. If the original ECMAScript value was a non-numeric,
                non-boolean value like `[]` or `{}`, then the WGSL `bool` result may be different
                than if the ECMAScript value had been converted to IDL {{boolean}} directly.

            : If |T| is `i32`
            ::
                Return the WGSL `i32` value corresponding to the result of [=?=] converting |v| to
                [=converted to an IDL value|an IDL value=] of type [{{EnforceRange}}] {{long}}.

            : If |T| is `u32`
            ::
                Return the WGSL `u32` value corresponding to the result of [=?=] converting |v| to
                [=converted to an IDL value|an IDL value=] of type [{{EnforceRange}}] {{unsigned long}}.

            : If |T| is `f32`
            ::
                Return the WGSL `f32` value corresponding to the result of [=?=] converting |v| to
                [=converted to an IDL value|an IDL value=] of type {{float}}.

            : If |T| is `f16`
            ::
                1. Let |wgslF32| be the WGSL `f32` value corresponding to the result of [=?=] converting |v| to
                    [=converted to an IDL value|an IDL value=] of type {{float}}.
                1. Return <code>f16(|wgslF32|)</code>, the result of [=!=] converting the WGSL `f32` value
                    to `f16` as defined in [=WGSL floating point conversion=].

                Note: As long as the value is in-range of `f32`, no error is thrown, even if the
                value is out-of-range of `f16`.
        </dl>
</div>

<div algorithm data-timeline=device>
    To convert a {{GPUColor}} |color| <dfn abstract-op>to a texel value of texture format</dfn> |format|,
    possibly throwing a {{TypeError}}, run the following [=device timeline=] steps:

    Note: This {{TypeError}} is generated in the [=device timeline=] and never surfaced to JavaScript.

    1. If the components of |format| ([=assert=] they all have the same type) are:

        <dl class=switch>
            : floating-point types or normalized types
            :: Let |T| be `f32`.
            : signed integer types
            :: Let |T| be `i32`.
            : unsigned integer types
            :: Let |T| be `u32`.
        </dl>

    1. Let |wgslColor| be a WGSL value of type <code>vec4&lt;|T|&gt;</code>, where the 4
        components are the RGBA channels of |color|, each [=?=] converted [$to WGSL type$] |T|.

    1. Convert |wgslColor| to |format| using the same conversion rules as the [[#output-merging]]
        step, and return the result.

        Note:
        For non-integer types, the exact choice of value is [=implementation-defined=].
        For normalized types, the value is clamped to the range of the type.

    Note:
    In other words, the value written will be as if it was written by a WGSL shader that
    outputs the value represented as a `vec4` of `f32`, `i32`, or `u32`.
</div>


# Initialization # {#initialization}

## navigator.gpu ## {#navigator-gpu}

A {{GPU}} object is available in the {{Window}} and {{WorkerGlobalScope}} contexts through the
{{Navigator}} and {{WorkerNavigator}} interfaces respectively and is exposed via `navigator.gpu`:

<script type=idl>
interface mixin NavigatorGPU {
    [SameObject, SecureContext] readonly attribute GPU gpu;
};
Navigator includes NavigatorGPU;
WorkerNavigator includes NavigatorGPU;
</script>

{{NavigatorGPU}} has the following attributes:

<dl dfn-type=attribute dfn-for=NavigatorGPU data-timeline=content>
    : <dfn>gpu</dfn>
    ::
        A global singleton providing top-level entry points like {{GPU/requestAdapter()}}.
</dl>

## GPU ## {#gpu-interface}

<dfn interface>GPU</dfn> is the entry point to WebGPU.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPU {
    Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
    GPUTextureFormat getPreferredCanvasFormat();
    [SameObject] readonly attribute WGSLLanguageFeatures wgslLanguageFeatures;
};
</script>

{{GPU}} has the following methods:

<dl dfn-type=method dfn-for=GPU>
    : <dfn>requestAdapter(options)</dfn>
    ::
        Requests an [=adapter=] from the user agent.
        The user agent chooses whether to return an adapter, and, if so,
        chooses according to the provided options.

        <div algorithm=GPU.requestAdapter>
            <div data-timeline=content>
                **Called on:** {{GPU}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPU/requestAdapter(options)">
                    |options|: Criteria used to select the adapter.
                </pre>

                **Returns:** {{Promise}}&lt;{{GPUAdapter}}?&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. All of the requirements in the following steps |must| be met.

                    <div class=validusage>
                        1. |options|.{{GPURequestAdapterOptions/featureLevel}} |must| be
                            a [=feature level string=].
                    </div>

                    If they are met **and** the user agent chooses to return an adapter:

                    1. Set |adapter| to an [=adapter=] chosen according to
                        the rules in [[#adapter-selection]] and the criteria in |options|,
                        adhering to [[#adapter-capability-guarantees]].
                        Initialize the properties of |adapter| according to their definitions:

                        1. Set |adapter|.{{adapter/[[limits]]}} and |adapter|.{{adapter/[[features]]}}
                            according to the supported capabilities of the adapter.
                            |adapter|.{{adapter/[[features]]}} must contain {{GPUFeatureName/"core-features-and-limits"}}.
                        1. If |adapter| meets the criteria of a [=fallback adapter=] set
                            |adapter|.{{adapter/[[fallback]]}} to `true`. Otherwise, set it to `false`.
                        1. Set |adapter|.{{adapter/[[xrCompatible]]}} to
                            |options|.{{GPURequestAdapterOptions/xrCompatible}}.

                    Otherwise:

                    1. Let |adapter| be `null`.
                1. Issue the subsequent steps on <var data-timeline=content>contentTimeline</var>.
            </div>
            <div data-timeline=content>
                [=Content timeline=] steps:

                1. If |adapter| is not `null`:
                    1. [=Resolve=] |promise| with a new {{GPUAdapter}} encapsulating |adapter|.

                    Otherwise:
                    1. [=Resolve=] |promise| with `null`.
            </div>
            <!-- If we add ways to make invalid adapter requests (aside from those
                that violate IDL rules), specify that they reject the promise. -->
        </div>

    : <dfn>getPreferredCanvasFormat()</dfn>
    ::
        Returns an optimal {{GPUTextureFormat}} for displaying 8-bit depth, standard dynamic range
        content on this system. Must only return {{GPUTextureFormat/"rgba8unorm"}} or
        {{GPUTextureFormat/"bgra8unorm"}}.

        The returned value can be passed as the {{GPUCanvasConfiguration/format}} to
        {{GPUCanvasContext/configure()}} calls on a {{GPUCanvasContext}} to ensure the associated
        canvas is able to display its contents efficiently.

        Note: Canvases which are not displayed to the screen may or may not benefit from using this
        format.

        <div algorithm=GPU.getPreferredCanvasFormat>
            <div data-timeline=content>
                **Called on:** {{GPU}} this.

                **Returns:** {{GPUTextureFormat}}

                [=Content timeline=] steps:

                1. Return either {{GPUTextureFormat/"rgba8unorm"}} or
                    {{GPUTextureFormat/"bgra8unorm"}}, depending on which format is optimal for
                    displaying WebGPU canvases on this system.
            </div>
        </div>
</dl>

{{GPU}} has the following attributes:

<dl dfn-type=attribute dfn-for=GPU data-timeline=content>
    : <dfn>wgslLanguageFeatures</dfn>
    ::
        The names of supported WGSL [=language extensions=].
        Supported language extensions are automatically enabled.
</dl>

[=Adapters=] **may** [$expire$] at any time. Upon any change in the system's state that could affect
the result of any {{GPU/requestAdapter()}} call, the user agent **should** [$expire$] all
previously-returned [=adapters=]. For example:

- A physical adapter is added/removed (via plug/unplug, driver update, hang recovery, etc.)
- The system's power configuration has changed (laptop unplugged, power settings changed, etc.)

Note:
User agents may choose to [$expire$] [=adapters=] often, even when there has been no system
state change (e.g. seconds or minutes after the adapter was created).
This can help obfuscate real system state changes, and make developers more aware that calling
{{GPU/requestAdapter()}} again is always necessary before calling {{GPUAdapter/requestDevice()}}.
If an application does encounter this situation, standard device-loss recovery
handling should allow it to recover.

<div class=example>
    Requesting a {{GPUAdapter}} with no hints:

    <pre highlight=js>
        const gpuAdapter = await navigator.gpu.requestAdapter();
    </pre>
</div>

### Adapter Capability Guarantees ### {#adapter-capability-guarantees}

Any {{GPUAdapter}} returned by {{GPU/requestAdapter()}} must provide the following guarantees:

- At least one of the following must be true:
    - {{GPUFeatureName/"texture-compression-bc"}} is supported.
    - Both {{GPUFeatureName/"texture-compression-etc2"}} and
        {{GPUFeatureName/"texture-compression-astc"}} are supported.
- If {{GPUFeatureName/"texture-compression-bc-sliced-3d"}}
    is supported, then {{GPUFeatureName/"texture-compression-bc"}} must be supported.
- If {{GPUFeatureName/"texture-compression-astc-sliced-3d"}}
    is supported, then {{GPUFeatureName/"texture-compression-astc"}} must be supported.
- All supported limits must be either the [=limit/default=] value or [=limit/better=].
- All [=limit class/alignment|alignment-class=] limits must be powers of 2.
- {{supported limits/maxBindingsPerBindGroup}} must be must be &ge;
    ([=max bindings per shader stage=] &times; [=max shader stages per pipeline=]), where:

    - <dfn dfn for="">max bindings per shader stage</dfn> is
        ({{supported limits/maxSampledTexturesPerShaderStage}} +
        {{supported limits/maxSamplersPerShaderStage}} +
        {{supported limits/maxStorageBuffersPerShaderStage}} +
        {{supported limits/maxStorageTexturesPerShaderStage}} +
        {{supported limits/maxUniformBuffersPerShaderStage}}).
    - <dfn dfn for="">max shader stages per pipeline</dfn> is `2`, because a
        {{GPURenderPipeline}} supports both a vertex and fragment shader.

    Note: {{supported limits/maxBindingsPerBindGroup}} does not reflect a fundamental limit;
    implementations should raise it to conform to this requirement, rather than lowering the
    other limits.

- {{supported limits/maxBindGroups}} must be &le; {{supported limits/maxBindGroupsPlusVertexBuffers}}.
- {{supported limits/maxVertexBuffers}} must be &le; {{supported limits/maxBindGroupsPlusVertexBuffers}}.
- {{supported limits/minUniformBufferOffsetAlignment}} and
    {{supported limits/minStorageBufferOffsetAlignment}} must both be &ge; 32 bytes.

        Note: 32 bytes would be the alignment of `vec4<f64>`. See [[WGSL#alignment-and-size]].
- {{supported limits/maxUniformBufferBindingSize}} must be &le; {{supported limits/maxBufferSize}}.
- {{supported limits/maxStorageBufferBindingSize}} must be &le; {{supported limits/maxBufferSize}}.
- {{supported limits/maxStorageBufferBindingSize}} must be a multiple of 4 bytes.
- {{supported limits/maxVertexBufferArrayStride}} must be a multiple of 4 bytes.
- {{supported limits/maxComputeWorkgroupSizeX}} must be &le; {{supported limits/maxComputeInvocationsPerWorkgroup}}.
- {{supported limits/maxComputeWorkgroupSizeY}} must be &le; {{supported limits/maxComputeInvocationsPerWorkgroup}}.
- {{supported limits/maxComputeWorkgroupSizeZ}} must be &le; {{supported limits/maxComputeInvocationsPerWorkgroup}}.
- {{supported limits/maxComputeInvocationsPerWorkgroup}} must be &le; {{supported limits/maxComputeWorkgroupSizeX}}
    &times; {{supported limits/maxComputeWorkgroupSizeY}} &times; {{supported limits/maxComputeWorkgroupSizeZ}}.

### Adapter Selection ### {#adapter-selection}

<dfn dictionary>GPURequestAdapterOptions</dfn>
provides hints to the user agent indicating what
configuration is suitable for the application.

<script type=idl>
dictionary GPURequestAdapterOptions {
    DOMString featureLevel = "core";
    GPUPowerPreference powerPreference;
    boolean forceFallbackAdapter = false;
    boolean xrCompatible = false;
};
</script>

<script type=idl>
enum GPUPowerPreference {
    "low-power",
    "high-performance",
};
</script>

{{GPURequestAdapterOptions}} has the following members:

<dl dfn-type=dict-member dfn-for=GPURequestAdapterOptions>
    : <dfn>featureLevel</dfn>
    ::
        "Feature level" for the adapter request.

        The allowed <dfn dfn for="">feature level string</dfn> values are:

        <dl dfn-type=dfn dfn-for="feature level string">
            : <dfn noexport>"core"</dfn>
            :: No effect.
            : <dfn noexport>"compatibility"</dfn>
            :: No effect.

                Note:
                This value is reserved for future use as a way to opt into additional validation restrictions.
                Applications should not use this value at this time.
        </dl>

    : <dfn>powerPreference</dfn>
    ::
        Optionally provides a hint indicating what class of [=adapter=] should be selected from
        the system's available adapters.

        The value of this hint may influence which adapter is chosen, but it must not
        influence whether an adapter is returned or not.

        Note:
        The primary utility of this hint is to influence which GPU is used in a multi-GPU system.
        For instance, some laptops have a low-power integrated GPU and a high-performance
        discrete GPU. This hint may also affect the power configuration of the selected GPU to
        match the requested power preference.

        Note:
        Depending on the exact hardware configuration, such as battery status and attached displays
        or removable GPUs, the user agent may select different [=adapters=] given the same power
        preference.
        Typically, given the same hardware configuration and state and
        `powerPreference`, the user agent is likely to select the same adapter.

        It must be one of the following values:

        <dl dfn-type=enum-value dfn-for=GPUPowerPreference>
            : `undefined` (or not present)
            ::
                Provides no hint to the user agent.

            : <dfn>"low-power"</dfn>
            ::
                Indicates a request to prioritize power savings over performance.

                Note:
                Generally, content should use this if it is unlikely to be constrained by drawing
                performance; for example, if it renders only one frame per second, draws only relatively
                simple geometry with simple shaders, or uses a small HTML canvas element.
                Developers are encouraged to use this value if their content allows, since it may
                significantly improve battery life on portable devices.

            : <dfn>"high-performance"</dfn>
            ::
                Indicates a request to prioritize performance over power consumption.

                Note:
                By choosing this value, developers should be aware that, for [=devices=] created on the
                resulting adapter, user agents are more likely to force device loss, in order to save
                power by switching to a lower-power adapter.
                Developers are encouraged to only specify this value if they believe it is absolutely
                necessary, since it may significantly decrease battery life on portable devices.
        </dl>

    : <dfn>forceFallbackAdapter</dfn>
    ::
        When set to `true` indicates that only a [=fallback adapter=] may be returned. If the user
        agent does not support a [=fallback adapter=], will cause {{GPU/requestAdapter()}} to
        resolve to `null`.

        Note:
        {{GPU/requestAdapter()}} may still return a [=fallback adapter=] if
        {{GPURequestAdapterOptions/forceFallbackAdapter}} is set to `false` and either no
        other appropriate [=adapter=] is available or the user agent chooses to return a
        [=fallback adapter=]. Developers that wish to prevent their applications from running on
        [=fallback adapters=] should check the
        {{GPUAdapter/info}}.{{GPUAdapterInfo/isFallbackAdapter}} attribute prior
        to requesting a {{GPUDevice}}.

    : <dfn>xrCompatible</dfn>
    ::
        When set to `true` indicates that the best [=adapter=] for rendering to a [=WebXR session=]
        must be returned. If the user agent or system does not support [=WebXR sessions=] then
        adapter selection may ignore this value.

        Note:
        If {{GPURequestAdapterOptions/xrCompatible}} is not set to `true` when the adapter is
        requested, {{GPUDevice}}s created from the adapter cannot be used to render for
        [=WebXR sessions=].
</dl>

<div class=example>
    Requesting a {{GPUPowerPreference/"high-performance"}} {{GPUAdapter}}:

    <pre highlight=js>
        const gpuAdapter = await navigator.gpu.requestAdapter({
            powerPreference: 'high-performance'
        });
    </pre>
</div>

<h3 id=gpuadapter data-dfn-type=interface>`GPUAdapter`
<span id=gpu-adapter></span>
</h3>

A {{GPUAdapter}} encapsulates an [=adapter=],
and describes its capabilities ([=features=] and [=limits=]).

To get a {{GPUAdapter}}, use {{GPU/requestAdapter()}}.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUAdapter {
    [SameObject] readonly attribute GPUSupportedFeatures features;
    [SameObject] readonly attribute GPUSupportedLimits limits;
    [SameObject] readonly attribute GPUAdapterInfo info;

    Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
};
</script>

{{GPUAdapter}} has the following [=immutable properties=]

<dl dfn-type=attribute dfn-for=GPUAdapter data-timeline=const>
    : <dfn>features</dfn>
    ::
        The set of values in `this`.{{GPUAdapter/[[adapter]]}}.{{adapter/[[features]]}}.

    : <dfn>limits</dfn>
    ::
        The limits in `this`.{{GPUAdapter/[[adapter]]}}.{{adapter/[[limits]]}}.

    : <dfn>info</dfn>
    ::
        Information about the physical adapter underlying this {{GPUAdapter}}.

        For a given {{GPUAdapter}}, the {{GPUAdapterInfo}} values exposed are constant over time.

        The same object is returned each time. To create that object for the first time:

        <div algorithm=GPUAdapter.info>
            <div data-timeline=content>
                **Called on:** {{GPUAdapter}} |this|.

                **Returns:** {{GPUAdapterInfo}}

                [=Content timeline=] steps:

                1. Return a [$new adapter info$] for |this|.{{GPUAdapter/[[adapter]]}}.
            </div>
        </div>

    : <dfn>\[[adapter]]</dfn>, of type [=adapter=], readonly
    ::
        The [=adapter=] to which this {{GPUAdapter}} refers.
</dl>

{{GPUAdapter}} has the following methods:

<dl dfn-type=method dfn-for=GPUAdapter>
    : <dfn>requestDevice(descriptor)</dfn>
    ::
        Requests a [=device=] from the [=adapter=].

        This is a one-time action: if a device is returned successfully,
        the adapter becomes {{adapter/[[state]]/"consumed"}}.

        <div algorithm=GPUAdapter.requestDevice>
            <div data-timeline=content>
                **Called on:** {{GPUAdapter}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUAdapter/requestDevice(descriptor)">
                    |descriptor|: Description of the {{GPUDevice}} to request.
                </pre>

                **Returns:** {{Promise}}&lt;{{GPUDevice}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Let |adapter| be |this|.{{GPUAdapter/[[adapter]]}}.
                1. Issue the |initialization steps| to the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following requirements are unmet:

                    <div class=validusage>
                        - The set of values in |descriptor|.{{GPUDeviceDescriptor/requiredFeatures}}
                            must be a subset of those in |adapter|.{{adapter/[[features]]}}.
                    </div>

                    Then issue the following steps on <var data-timeline=content>contentTimeline</var>
                    and return:

                        <div data-timeline=content>
                            [=Content timeline=] steps:

                            1. [=Reject=] |promise| with a {{TypeError}}.
                        </div>

                    Note: This is the same error that is produced if a feature name isn't known
                    by the browser at all (in its {{GPUFeatureName}} definition).
                    This converges the behavior when the browser doesn't support a feature
                    with the behavior when a particular adapter doesn't support a feature.

                1. All of the requirements in the following steps |must| be met.

                    <div class=validusage>
                        1. |adapter|.{{adapter/[[state]]}} must not be {{adapter/[[state]]/"consumed"}}.
                        1. For each [|key|, |value|] in |descriptor|.{{GPUDeviceDescriptor/requiredLimits}}
                            for which |value| is not `undefined`:
                            1. |key| |must| be the name of a member of [=supported limits=].
                            1. |value| |must| be no [=limit/better=] than |adapter|.{{adapter/[[limits]]}}[|key|].
                            1. If |key|'s [=limit class|class=] is [=limit class/alignment=],
                                |value| |must| be a power of 2 less than 2<sup>32</sup>.

                            Note:
                            User agents should consider issuing developer-visible warnings when
                            |key| is not recognized, even when |value| is `undefined`.
                    </div>

                    If any are unmet, issue the following steps on <var data-timeline=content>contentTimeline</var>
                    and return:

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with an {{OperationError}}.
                    </div>

                1. If |adapter|.{{adapter/[[state]]}} is {{adapter/[[state]]/"expired"}}
                    or the user agent otherwise cannot fulfill the request:

                    1. Let |device| be a new [=device=].
                    1. [=Lose the device=](|device|, {{GPUDeviceLostReason/"unknown"}}).
                    1. [=Assert=] |adapter|.{{adapter/[[state]]}} is {{adapter/[[state]]/"expired"}}.

                        Note:
                        User agents should consider issuing developer-visible warnings in
                        most or all cases when this occurs. Applications should perform
                        reinitialization logic starting with {{GPU/requestAdapter()}}.

                    Otherwise:

                    1. Let |device| be [=a new device=] with the capabilities described by |descriptor|.
                    1. [$Expire$] |adapter|.

                1. Issue the subsequent steps on <var data-timeline=content>contentTimeline</var>.
            </div>
            <div data-timeline=content>
                [=Content timeline=] steps:

                <!--This is a variant of [$create a new WebGPU object$] that accounts for the fact
                    that the device has no parent.-->
                1. Let |gpuDevice| be a new {{GPUDevice}} instance.
                1. Set |gpuDevice|.{{GPUObjectBase/[[device]]}} to |device|.
                1. Set |device|.{{device/[[content device]]}} to |gpuDevice|.
                1. Set |gpuDevice|.{{GPUObjectBase/label}} to |descriptor|.{{GPUObjectDescriptorBase/label}}.

                1. [=Resolve=] |promise| with |gpuDevice|.

                    Note:
                    If the device is already lost because the adapter could not fulfill the request,
                    |device|.{{GPUDevice/lost}} has already resolved before |promise| resolves.
            </div>
        </div>
</dl>

<div class=example>
    Requesting a {{GPUDevice}} with default features and limits:

    <pre highlight=js>
        const gpuAdapter = await navigator.gpu.requestAdapter();
        const gpuDevice = await gpuAdapter.requestDevice();
    </pre>
</div>

<h4 id=gpudevicedescriptor data-dfn-type=dictionary>`GPUDeviceDescriptor`
<span id=dictdef-gpudevicedescriptor></span>
</h4>

{{GPUDeviceDescriptor}} describes a device request.

<script type=idl>
dictionary GPUDeviceDescriptor
         : GPUObjectDescriptorBase {
    sequence<GPUFeatureName> requiredFeatures = [];
    record<DOMString, (GPUSize64 or undefined)> requiredLimits = {};
    GPUQueueDescriptor defaultQueue = {};
};
</script>

{{GPUDeviceDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUDeviceDescriptor>
    : <dfn>requiredFeatures</dfn>
    ::
        Specifies the [=features=] that are required by the device request.
        The request will fail if the adapter cannot provide these features.

        Exactly the specified set of features, and no more or less, will be allowed in validation
        of API calls on the resulting device.

    : <dfn>requiredLimits</dfn>
    ::
        Specifies the [=limits=] that are required by the device request.
        The request will fail if the adapter cannot provide these limits.

        Each key with a non-`undefined` value must be the name of a member of [=supported limits=].

        API calls on the resulting device perform validation according to the exact limits of the
        device (not the adapter; see [[#limits]]).

        <!-- If we ever need limit types other than GPUSize32/GPUSize64, we can change the value
        type to `double` or `any` in the future and write out the type conversion explicitly (by
        reference to WebIDL spec). Or change the entire type to `any` and add back a `dictionary
        GPULimits` and define the conversion of the whole object by reference to WebIDL. -->

    : <dfn>defaultQueue</dfn>
    ::
        The descriptor for the default {{GPUQueue}}.
</dl>

<div class=example>
    Requesting a {{GPUDevice}} with the {{GPUFeatureName/"texture-compression-astc"}} feature if supported:

    <pre highlight=js>
        const gpuAdapter = await navigator.gpu.requestAdapter();

        const requiredFeatures = [];
        if (gpuAdapter.features.has('texture-compression-astc')) {
            requiredFeatures.push('texture-compression-astc')
        }

        const gpuDevice = await gpuAdapter.requestDevice({
            requiredFeatures
        });
    </pre>
</div>

<div class=example>
    Requesting a {{GPUDevice}} with a higher {{supported limits/maxColorAttachmentBytesPerSample}} limit:

    <pre highlight=js>
        const gpuAdapter = await navigator.gpu.requestAdapter();

        if (gpuAdapter.limits.maxColorAttachmentBytesPerSample < 64) {
            // When the desired limit isn't supported, take action to either fall back to a code
            // path that does not require the higher limit or notify the user that their device
            // does not meet minimum requirements.
        }

        // Request higher limit of max color attachments bytes per sample.
        const gpuDevice = await gpuAdapter.requestDevice({
            requiredLimits: { maxColorAttachmentBytesPerSample: 64 },
        });
    </pre>
</div>

<h5 id=gpufeaturename data-dfn-type=enum>`GPUFeatureName`
<span id=enumdef-gpufeaturename></span>
</h5>

Each {{GPUFeatureName}} identifies a set of functionality which, if available,
allows additional usages of WebGPU that would have otherwise been invalid.

<script type=idl>
enum GPUFeatureName {
    "core-features-and-limits",
    "depth-clip-control",
    "depth32float-stencil8",
    "texture-compression-bc",
    "texture-compression-bc-sliced-3d",
    "texture-compression-etc2",
    "texture-compression-astc",
    "texture-compression-astc-sliced-3d",
    "timestamp-query",
    "indirect-first-instance",
    "shader-f16",
    "rg11b10ufloat-renderable",
    "bgra8unorm-storage",
    "float32-filterable",
    "float32-blendable",
    "clip-distances",
    "dual-source-blending",
    "subgroups",
    "texture-formats-tier1",
    "texture-formats-tier2",
    "primitive-index",
};
</script>

<h3 id=gpudevice data-dfn-type=interface>`GPUDevice`
<span id=gpu-device></span>
</h3>

A {{GPUDevice}} encapsulates a [=device=] and exposes
the functionality of that device.

{{GPUDevice}} is the top-level interface through which [=WebGPU interfaces=] are created.

To get a {{GPUDevice}}, use {{GPUAdapter/requestDevice()}}.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUDevice : EventTarget {
    [SameObject] readonly attribute GPUSupportedFeatures features;
    [SameObject] readonly attribute GPUSupportedLimits limits;
    [SameObject] readonly attribute GPUAdapterInfo adapterInfo;

    [SameObject] readonly attribute GPUQueue queue;

    undefined destroy();

    GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
    GPUTexture createTexture(GPUTextureDescriptor descriptor);
    GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
    GPUExternalTexture importExternalTexture(GPUExternalTextureDescriptor descriptor);

    GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
    GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
    GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);

    GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
    GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
    GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
    Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
    Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);

    GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
    GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);

    GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
};
GPUDevice includes GPUObjectBase;
</script>

{{GPUDevice}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUDevice data-timeline=const>
    : <dfn>features</dfn>
    ::
        A set containing the {{GPUFeatureName}} values of the features
        supported by the device ({{GPUObjectBase/[[device]]}}.{{device/[[features]]}}).

    : <dfn>limits</dfn>
    ::
        The limits supported by the device ({{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}).

    : <dfn>queue</dfn>
    ::
        The primary {{GPUQueue}} for this device.

    : <dfn>adapterInfo</dfn>
    ::
        Information about the physical adapter which created the [=device=] that this {{GPUDevice}} refers to.

        For a given {{GPUDevice}}, the {{GPUAdapterInfo}} values exposed are constant over time.

        The same object is returned each time. To create that object for the first time:

        <div algorithm=GPUDevice.adapterInfo>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Returns:** {{GPUAdapterInfo}}

                [=Content timeline=] steps:

                1. Return a [$new adapter info$] for |this|.{{GPUObjectBase/[[device]]}}.{{device/[[adapter]]}}.
            </div>
        </div>
</dl>

The {{GPUObjectBase/[[device]]}} for a {{GPUDevice}} is the [=device=] that the {{GPUDevice}} refers
to.

{{GPUDevice}} has the following methods:

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>destroy()</dfn>
    ::
        Destroys the [=device=], preventing further operations on it.
        Outstanding asynchronous operations will fail.

        Note: It is valid to destroy a device multiple times.

        <div algorithm=GPUDevice.destroy()>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                [=Content timeline=] steps:

                1. {{GPUBuffer/unmap()}} all {{GPUBuffer}}s from this device.

                    <!-- POSTV1(multithreading) tentative text:
                    ... which are mapped in this [=agent=] (thread).

                    Note: Any buffers which are mapped in a different thread are not unmapped.
                    They can be unmapped only from the thread on which they are mapped, either by
                    another call to {{GPUDevice/destroy()|GPUDevice.destroy()}}, or by
                    {{GPUBuffer/destroy()|GPUBuffer.destroy()}} or {{GPUBuffer/unmap()|GPUBuffer.unmap()}}.
                    -->
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                1. [=Lose the device=](|this|.{{GPUObjectBase/[[device]]}},
                    {{GPUDeviceLostReason/"destroyed"}}).
            </div>
        </div>

        Note: Since no further operations can be enqueued on this device, implementations can abort
        outstanding asynchronous operations immediately and free resource allocations, including
        mapped memory that was just unmapped.
</dl>

<div algorithm data-timeline=const>
    A {{GPUDevice}}'s <dfn dfn>allowed buffer usages</dfn> are:

    - Always allowed:
        {{GPUBufferUsage/MAP_READ}},
        {{GPUBufferUsage/MAP_WRITE}},
        {{GPUBufferUsage/COPY_SRC}},
        {{GPUBufferUsage/COPY_DST}},
        {{GPUBufferUsage/INDEX}},
        {{GPUBufferUsage/VERTEX}},
        {{GPUBufferUsage/UNIFORM}},
        {{GPUBufferUsage/STORAGE}},
        {{GPUBufferUsage/INDIRECT}},
        {{GPUBufferUsage/QUERY_RESOLVE}}

    <!-- As needed, compute more allowed usages based on the features enabled on the device. -->
</div>

<div algorithm data-timeline=const>
    A {{GPUDevice}}'s <dfn dfn>allowed texture usages</dfn> are:

    - Always allowed:
        {{GPUTextureUsage/COPY_SRC}},
        {{GPUTextureUsage/COPY_DST}},
        {{GPUTextureUsage/TEXTURE_BINDING}},
        {{GPUTextureUsage/STORAGE_BINDING}},
        {{GPUTextureUsage/RENDER_ATTACHMENT}}

    <!-- As needed, compute more allowed usages based on the features enabled on the device. -->
</div>

## Example ## {#initialization-examples}

<div class=example>
    A more robust example of requesting a {{GPUAdapter}} and {{GPUDevice}} with error handling:

    <pre highlight=js>
        let gpuDevice = null;

        async function initializeWebGPU() {
            // Check to ensure the user agent supports WebGPU.
            if (!('gpu' in navigator)) {
                console.error("User agent doesn't support WebGPU.");
                return false;
            }

            // Request an adapter.
            const gpuAdapter = await navigator.gpu.requestAdapter();

            // requestAdapter may resolve with null if no suitable adapters are found.
            if (!gpuAdapter) {
                console.error('No WebGPU adapters found.');
                return false;
            }

            // Request a device.
            // Note that the promise will reject if invalid options are passed to the optional
            // dictionary. To avoid the promise rejecting always check any features and limits
            // against the adapters features and limits prior to calling requestDevice().
            gpuDevice = await gpuAdapter.requestDevice();

            // requestDevice will never return null, but if a valid device request can't be
            // fulfilled for some reason it may resolve to a device which has already been lost.
            // Additionally, devices can be lost at any time after creation for a variety of reasons
            // (ie: browser resource management, driver updates), so it's a good idea to always
            // handle lost devices gracefully.
            gpuDevice.lost.then((info) => {
                console.error(\`WebGPU device was lost: ${info.message}\`);

                gpuDevice = null;

                // Many causes for lost devices are transient, so applications should try getting a
                // new device once a previous one has been lost unless the loss was caused by the
                // application intentionally destroying the device. Note that any WebGPU resources
                // created with the previous device (buffers, textures, etc) will need to be
                // re-created with the new one.
                if (info.reason != 'destroyed') {
                    initializeWebGPU();
                }
            });

            onWebGPUInitialized();

            return true;
        }

        function onWebGPUInitialized() {
            // Begin creating WebGPU resources here...
        }

        initializeWebGPU();
    </pre>
</div>

# Buffers # {#buffers}

<h3 id=gpubuffer data-dfn-type=interface>`GPUBuffer`
<span id=buffer-interface></span>
</h3>

A {{GPUBuffer}} represents a block of memory that can be used in GPU operations.
Data is stored in linear layout, meaning that each byte of the allocation can be
addressed by its offset from the start of the {{GPUBuffer}}, subject to alignment
restrictions depending on the operation. Some {{GPUBuffer|GPUBuffers}} can be
mapped which makes the block of memory accessible via an {{ArrayBuffer}} called
its mapping.

{{GPUBuffer}}s are created via {{GPUDevice/createBuffer()}}.
Buffers may be {{GPUBufferDescriptor/mappedAtCreation}}.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUBuffer {
    readonly attribute GPUSize64Out size;
    readonly attribute GPUFlagsConstant usage;

    readonly attribute GPUBufferMapState mapState;

    Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
    ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
    undefined unmap();

    undefined destroy();
};
GPUBuffer includes GPUObjectBase;

enum GPUBufferMapState {
    "unmapped",
    "pending",
    "mapped",
};
</script>

{{GPUBuffer}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUBuffer data-timeline=const>
    : <dfn>size</dfn>
    ::
        The length of the {{GPUBuffer}} allocation in bytes.

    : <dfn>usage</dfn>
    ::
        The allowed usages for this {{GPUBuffer}}.
</dl>

{{GPUBuffer}} has the following [=content timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBuffer data-timeline=content>
    : <dfn>mapState</dfn>
    ::
        The current <dfn enum for="">GPUBufferMapState</dfn> of the buffer:

        <dl dfn-type=enum-value dfn-for=GPUBufferMapState>
            : <dfn>"unmapped"</dfn>
            ::
                The buffer is not mapped for use by `this`.{{GPUBuffer/getMappedRange()}}.

            : <dfn>"pending"</dfn>
            ::
                A mapping of the buffer has been requested, but is pending.
                It may succeed, or fail validation in {{GPUBuffer/mapAsync()}}.

            : <dfn>"mapped"</dfn>
            ::
                The buffer is mapped and `this`.{{GPUBuffer/getMappedRange()}} may be used.
        </dl>

        The [=getter steps=] are:

        <div algorithm=mapState>
            <div data-timeline=content>
                [=Content timeline=] steps:

                1. If |this|.{{GPUBuffer/[[mapping]]}} is not `null`,
                    return {{GPUBufferMapState/"mapped"}}.
                1. If |this|.{{GPUBuffer/[[pending_map]]}} is not `null`,
                    return {{GPUBufferMapState/"pending"}}.
                1. Return {{GPUBufferMapState/"unmapped"}}.
            </div>
        </div>

    : <dfn>\[[pending_map]]</dfn>, of type {{Promise}}&lt;void&gt; or `null`, initially `null`
    ::
        The {{Promise}} returned by the currently-pending {{GPUBuffer/mapAsync()}} call.

        There is never more than one pending map, because {{GPUBuffer/mapAsync()}}
        will refuse immediately if a request is already in flight.

    : <dfn>\[[mapping]]</dfn>, of type [=active buffer mapping=] or `null`, initially `null`
    ::
        Set if and only if the buffer is currently mapped for use by {{GPUBuffer/getMappedRange()}}.
        Null otherwise (even if there is a {{GPUBuffer/[[pending_map]]}}).

        An <dfn dfn for="">active buffer mapping</dfn> is a structure with the following fields:

        <dl dfn-type=dfn dfn-for="active buffer mapping">
            : <dfn>data</dfn>, of type [=Data Block=]
            ::
                The mapping for this {{GPUBuffer}}. This data is accessed through {{ArrayBuffer}}s
                which are views onto this data, returned by {{GPUBuffer/getMappedRange()}} and
                stored in [=active buffer mapping/views=].

            : <dfn>mode</dfn>, of type {{GPUMapModeFlags}}
            ::
                The {{GPUMapModeFlags}} of the map, as specified in the corresponding call to
                {{GPUBuffer/mapAsync()}} or {{GPUDevice/createBuffer()}}.

            : <dfn>range</dfn>, of type tuple [{{unsigned long long}}, {{unsigned long long}}]
            ::
                The range of this {{GPUBuffer}} that is mapped.

            : <dfn>views</dfn>, of type [=list=]&lt;{{ArrayBuffer}}&gt;
            ::
                The {{ArrayBuffer}}s returned via {{GPUBuffer/getMappedRange()}} to the application.
                They are tracked so they can be detached when {{GPUBuffer/unmap()}} is called.
        </dl>

        <div algorithm data-timeline=content>
            To <dfn abstract-op for="">initialize an active buffer mapping</dfn> with mode |mode| and
            range |range|, run the following [=content timeline=] steps:

            1. Let |size| be |range|[1] - |range|[0].
            1. Let |data| be [=?=] [$CreateByteDataBlock$](|size|).

                <div class=note heading>
                    This may result in a {{RangeError}} being thrown.
                    For consistency and predictability:

                    - For any size at which `new ArrayBuffer()` would succeed at a given moment,
                        this allocation **should** succeed at that moment.
                    - For any size at which `new ArrayBuffer()` *deterministically* throws a
                        {{RangeError}}, this allocation **should** as well.
                </div>

            1. Return an [=active buffer mapping=] with:
                - [=active buffer mapping/data=] set to |data|.
                - [=active buffer mapping/mode=] set to |mode|.
                - [=active buffer mapping/range=] set to |range|.
                - [=active buffer mapping/views=] set to `[]`.
        </div>
</dl>

<div class=example>
    <figure>
        <figcaption>Mapping and unmapping a buffer.</figcaption>

        <object type="image/svg+xml" data="img/buffer-map-unmap.mmd.svg"></object>
    </figure>

    <figure>
        <figcaption>Failing to map a buffer.</figcaption>

        <object type="image/svg+xml" data="img/buffer-map-failure.mmd.svg"></object>
    </figure>
</div>

{{GPUBuffer}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBuffer data-timeline=device>
    : <dfn>[[internal state]]</dfn>
    ::
        The current internal state of the buffer:

        <dl dfn-type=dfn dfn-for="GPUBuffer/[[internal state]]">
            : "<dfn>available</dfn>"
            ::
                The buffer may be used in queue operations (unless it is [$invalid$]).

            : "<dfn>unavailable</dfn>"
            ::
                The buffer may not be used in queue operations due to being mapped.

            : "<dfn>destroyed</dfn>"
            ::
                The buffer may not be used in any operations due to being {{GPUBuffer/destroy()}}ed.
        </dl>
</dl>

<h4 id=gpubufferdescriptor data-dfn-type=dictionary>`GPUBufferDescriptor`
<span id=GPUBufferDescriptor></span>
<span id=dictdef-gpubufferdescriptor></span>
</h4>

<script type=idl>
dictionary GPUBufferDescriptor
         : GPUObjectDescriptorBase {
    required GPUSize64 size;
    required GPUBufferUsageFlags usage;
    boolean mappedAtCreation = false;
};
</script>

{{GPUBufferDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUBufferDescriptor>
    : <dfn>size</dfn>
    ::
        The size of the buffer in bytes.

    : <dfn>usage</dfn>
    ::
        The allowed usages for the buffer.

    : <dfn>mappedAtCreation</dfn>
    ::
        If `true` creates the buffer in an already mapped state, allowing
        {{GPUBuffer/getMappedRange()}} to be called immediately. It is valid to set
        {{GPUBufferDescriptor/mappedAtCreation}} to `true` even if {{GPUBufferDescriptor/usage}}
        does not contain {{GPUBufferUsage/MAP_READ}} or {{GPUBufferUsage/MAP_WRITE}}. This can be
        used to set the buffer's initial data.

        Guarantees that even if the buffer creation eventually fails, it will still appear as if the
        mapped range can be written/read to until it is unmapped.
</dl>

### Buffer Usages ### {#buffer-usage}

<script type=idl>
typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
[Exposed=(Window, Worker), SecureContext]
namespace GPUBufferUsage {
    const GPUFlagsConstant MAP_READ      = 0x0001;
    const GPUFlagsConstant MAP_WRITE     = 0x0002;
    const GPUFlagsConstant COPY_SRC      = 0x0004;
    const GPUFlagsConstant COPY_DST      = 0x0008;
    const GPUFlagsConstant INDEX         = 0x0010;
    const GPUFlagsConstant VERTEX        = 0x0020;
    const GPUFlagsConstant UNIFORM       = 0x0040;
    const GPUFlagsConstant STORAGE       = 0x0080;
    const GPUFlagsConstant INDIRECT      = 0x0100;
    const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
};
</script>

The {{GPUBufferUsage}} flags determine how a {{GPUBuffer}} may be used after its creation:

<dl dfn-type=const dfn-for=GPUBufferUsage>
    : <dfn>MAP_READ</dfn>
    ::
        The buffer can be mapped for reading. (Example: calling {{GPUBuffer/mapAsync()}} with
        {{GPUMapMode/READ|GPUMapMode.READ}})

        May only be combined with {{GPUBufferUsage/COPY_DST}}.

    : <dfn>MAP_WRITE</dfn>
    ::
        The buffer can be mapped for writing. (Example: calling {{GPUBuffer/mapAsync()}} with
        {{GPUMapMode/WRITE|GPUMapMode.WRITE}})

        May only be combined with {{GPUBufferUsage/COPY_SRC}}.

    : <dfn>COPY_SRC</dfn>
    ::
        The buffer can be used as the source of a copy operation. (Examples: as the `source`
        argument of a [=GPUCommandEncoder/copyBufferToBuffer()=] or
        {{GPUCommandEncoder/copyBufferToTexture()}} call.)

    : <dfn>COPY_DST</dfn>
    ::
        The buffer can be used as the destination of a copy or write operation. (Examples: as the
        `destination` argument of a [=GPUCommandEncoder/copyBufferToBuffer()=] or
        {{GPUCommandEncoder/copyTextureToBuffer()}} call, or as the target of a
        {{GPUQueue/writeBuffer()}} call.)

    : <dfn>INDEX</dfn>
    ::
        The buffer can be used as an index buffer. (Example: passed to
        {{GPURenderCommandsMixin/setIndexBuffer()}}.)

    : <dfn>VERTEX</dfn>
    ::
        The buffer can be used as a vertex buffer. (Example: passed to
        {{GPURenderCommandsMixin/setVertexBuffer()}}.)

    : <dfn>UNIFORM</dfn>
    ::
        The buffer can be used as a uniform buffer. (Example: as a bind group entry for a
        {{GPUBufferBindingLayout}} with a
        {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} of
        {{GPUBufferBindingType/"uniform"}}.)

    : <dfn>STORAGE</dfn>
    ::
        The buffer can be used as a storage buffer. (Example: as a bind group entry for a
        {{GPUBufferBindingLayout}} with a
        {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} of
        {{GPUBufferBindingType/"storage"}} or {{GPUBufferBindingType/"read-only-storage"}}.)

    : <dfn>INDIRECT</dfn>
    ::
        The buffer can be used as to store indirect command arguments. (Examples: as the
        `indirectBuffer` argument of a {{GPURenderCommandsMixin/drawIndirect()}} or
        {{GPUComputePassEncoder/dispatchWorkgroupsIndirect()}} call.)

    : <dfn>QUERY_RESOLVE</dfn>
    ::
        The buffer can be used to capture query results. (Example: as the `destination` argument of
        a {{GPUCommandEncoder/resolveQuerySet()}} call.)
</dl>

### Buffer Creation ### {#buffer-creation}

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createBuffer(descriptor)</dfn>
    ::
        Creates a {{GPUBuffer}}.

        <div algorithm=GPUDevice.createBuffer>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createBuffer(descriptor)">
                    |descriptor|: Description of the {{GPUBuffer}} to create.
                </pre>

                **Returns:** {{GPUBuffer}}

                [=Content timeline=] steps:

                1. Let |b| be [=!=] [$create a new WebGPU object$](|this|, {{GPUBuffer}}, |descriptor|).
                1. Set |b|.{{GPUBuffer/size}} to |descriptor|.{{GPUBufferDescriptor/size}}.
                1. Set |b|.{{GPUBuffer/usage}} to |descriptor|.{{GPUBufferDescriptor/usage}}.
                1. If |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is `true`:
                    1. If |descriptor|.{{GPUBufferDescriptor/size}} is not a multiple of 4,
                        throw a {{RangeError}}.
                    1. Set |b|.{{GPUBuffer/[[mapping]]}} to
                        [=?=] [$initialize an active buffer mapping$] with mode {{GPUMapMode/WRITE}}
                        and range <code>[0, |descriptor|.{{GPUBufferDescriptor/size}}]</code>.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |b|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following requirements are unmet,
                    [$generate a validation error$], [$invalidate$] |b| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - |descriptor|.{{GPUBufferDescriptor/usage}} must not be 0.
                        - |descriptor|.{{GPUBufferDescriptor/usage}} must be a subset of the
                            [=allowed buffer usages=] for |this|.
                        - If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_READ}}:
                            - |descriptor|.{{GPUBufferDescriptor/usage}} must contain no other flags
                                except {{GPUBufferUsage/COPY_DST}}.
                        - If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_WRITE}}:
                            - |descriptor|.{{GPUBufferDescriptor/usage}} must contain no other flags
                                except {{GPUBufferUsage/COPY_SRC}}.
                        - If |descriptor|.{{GPUBufferDescriptor/size}} must be &le;
                            |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxBufferSize}}.
                    </div>

                Note: If buffer creation fails, and |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is `false`,
                any calls to {{GPUBuffer/mapAsync()}} will reject, so any resources allocated to enable mapping can
                and may be discarded or recycled.

                1. If |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is `true`:
                    1. Set |b|.{{GPUBuffer/[[internal state]]}} to "[=GPUBuffer/[[internal state]]/unavailable=]".

                    Otherwise:

                    1. Set |b|.{{GPUBuffer/[[internal state]]}} to "[=GPUBuffer/[[internal state]]/available=]".

                1. Create a device allocation for |b| where each byte is zero.

                    If the allocation fails without side-effects,
                    [$generate an out-of-memory error$], [$invalidate$] |b|, and return.
            </div>
        </div>
</dl>

<div class=example>
    Creating a 128 byte uniform buffer that can be written into:

    <pre highlight=js>
        const buffer = gpuDevice.createBuffer({
            size: 128,
            usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
        });
    </pre>
</div>

### Buffer Destruction ### {#buffer-destruction}

An application that no longer requires a {{GPUBuffer}} can choose to lose
access to it before garbage collection by calling {{GPUBuffer/destroy()}}. Destroying a buffer also
unmaps it, freeing any memory allocated for the mapping.

Note: This allows the user agent to reclaim the GPU memory associated with the {{GPUBuffer}}
once all previously submitted operations using it are complete.

{{GPUBuffer}} has the following methods:

<dl dfn-type=method dfn-for=GPUBuffer>
    : <dfn>destroy()</dfn>
    ::
        Destroys the {{GPUBuffer}}.

        Note: It is valid to destroy a buffer multiple times.

        <div algorithm=GPUBuffer.destroy>
            <div data-timeline=content>
                **Called on:** {{GPUBuffer}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Call |this|.{{GPUBuffer/unmap()}}.

                <!-- POSTV1(multithreading) tentative text:
                    Note: If the buffer is mapped in a different thread, it is not unmapped.
                    It can be unmapped only from the thread on which it is mapped, either by
                    another call to {{GPUBuffer/destroy()|GPUBuffer.destroy()}},
                    or by {{GPUBuffer/unmap()|GPUBuffer.unmap()}}.
                -->

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Set |this|.{{GPUBuffer/[[internal state]]}} to
                    "[=GPUBuffer/[[internal state]]/destroyed=]".
            </div>
        </div>

        Note: Since no further operations can be enqueued using this buffer, implementations can
        free resource allocations, including mapped memory that was just unmapped.
</dl>

## Buffer Mapping ## {#buffer-mapping}

An application can request to map a {{GPUBuffer}} so that they can access its
content via {{ArrayBuffer}}s that represent part of the {{GPUBuffer}}'s
allocations. Mapping a {{GPUBuffer}} is requested asynchronously with
{{GPUBuffer/mapAsync()}} so that the user agent can ensure the GPU
finished using the {{GPUBuffer}} before the application can access its content.
A mapped {{GPUBuffer}}
cannot be used by the GPU and must be unmapped using {{GPUBuffer/unmap()}} before
work using it can be submitted to the [=Queue timeline=].

Once the {{GPUBuffer}} is mapped, the application can synchronously ask for access
to ranges of its content with {{GPUBuffer/getMappedRange()}}.
The returned {{ArrayBuffer}} can only be [=ArrayBuffer/detach|detached=] by {{GPUBuffer/unmap()}}
(directly, or via {{GPUBuffer}}.{{GPUBuffer/destroy()}} or {{GPUDevice}}.{{GPUDevice/destroy()}}),
and cannot be [=ArrayBuffer/transfer|transferred=].
A {{TypeError}} is thrown by any other operation that attempts to do so.

<!-- POSTV1(multithreading):
Add client-side validation that a mapped buffer can
only be unmapped and destroyed on the worker on which it was mapped. Likewise
{{GPUBuffer/getMappedRange()}} can only be called on that worker.
-->

<script type=idl>
typedef [EnforceRange] unsigned long GPUMapModeFlags;
[Exposed=(Window, Worker), SecureContext]
namespace GPUMapMode {
    const GPUFlagsConstant READ  = 0x0001;
    const GPUFlagsConstant WRITE = 0x0002;
};
</script>

The {{GPUMapMode}} flags determine how a {{GPUBuffer}} is mapped when calling
{{GPUBuffer/mapAsync()}}:

<dl dfn-type=const dfn-for=GPUMapMode>
    : <dfn>READ</dfn>
    ::
        Only valid with buffers created with the {{GPUBufferUsage/MAP_READ}} usage.

        Once the buffer is mapped, calls to {{GPUBuffer/getMappedRange()}} will return an
        {{ArrayBuffer}} containing the buffer's current values. Changes to the returned
        {{ArrayBuffer}} will be discarded after {{GPUBuffer/unmap()}} is called.

    : <dfn>WRITE</dfn>
    ::
        Only valid with buffers created with the {{GPUBufferUsage/MAP_WRITE}} usage.

        Once the buffer is mapped, calls to {{GPUBuffer/getMappedRange()}} will return an
        {{ArrayBuffer}} containing the buffer's current values. Changes to the returned
        {{ArrayBuffer}} will be stored in the {{GPUBuffer}} after {{GPUBuffer/unmap()}} is called.

        Note: Since the {{GPUBufferUsage/MAP_WRITE}} buffer usage may only be combined with the
        {{GPUBufferUsage/COPY_SRC}} buffer usage, mapping for writing can never return values
        produced by the GPU, and the returned {{ArrayBuffer}} will only ever contain the default
        initialized data (zeros) or data written by the webpage during a previous mapping.
</dl>

{{GPUBuffer}} has the following methods:

<dl dfn-type=method dfn-for=GPUBuffer>
    : <dfn>mapAsync(mode, offset, size)</dfn>
    ::
        Maps the given range of the {{GPUBuffer}} and resolves the returned {{Promise}} when the
        {{GPUBuffer}}'s content is ready to be accessed with {{GPUBuffer/getMappedRange()}}.

        The resolution of the returned {{Promise}} **only** indicates that the buffer has been mapped.
        It does not guarantee the completion of any other operations visible to the [=content timeline=],
        and in particular does not imply that any other {{Promise}} returned from
        {{GPUQueue/onSubmittedWorkDone()}} or {{GPUBuffer/mapAsync()}} on other {{GPUBuffer}}s
        have resolved.

        The resolution of the {{Promise}} returned from {{GPUQueue/onSubmittedWorkDone()}}
        **does** imply the completion of
        {{GPUBuffer/mapAsync()}} calls made prior to that call,
        on {{GPUBuffer}}s last used exclusively on that queue.

        <div algorithm=GPUBuffer.mapAsync>
            <div data-timeline=content>
                **Called on:** {{GPUBuffer}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUBuffer/mapAsync(mode, offset, size)">
                    |mode|: Whether the buffer should be mapped for reading or writing.
                    |offset|: Offset in bytes into the buffer to the start of the range to map.
                    |size|: Size in bytes of the range to map.
                </pre>

                **Returns:** {{Promise}}&lt;{{undefined}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. If |this|.{{GPUBuffer/mapState}} is not {{GPUBufferMapState/"unmapped"}}:
                    1. Issue the |early-reject steps| on the [=Device timeline=] of
                        |this|.{{GPUObjectBase/[[device]]}}.
                    1. Return [=a promise rejected with=] {{OperationError}}.
                1. Let |p| be a new {{Promise}}.
                1. Set |this|.{{GPUBuffer/[[pending_map]]}} to |p|.
                1. Issue the |validation steps| on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
                1. Return |p|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |early-reject steps|:

                1. [$Generate a validation error$].
                1. Return.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |validation steps|:

                1. If |size| is `undefined`:
                    1. Let |rangeSize| be max(0, |this|.{{GPUBuffer/size}} - |offset|).

                    Otherwise:

                    1. Let |rangeSize| be |size|.

                1. If any of the following conditions are unsatisfied:

                    <div class=validusage>
                        - |this| must be [$valid$].
                    </div>

                    1. Set |deviceLost| to `true`.
                    1. Issue the <var data-timeline=content>map failure steps</var> on
                        <var data-timeline=content>contentTimeline</var>.
                    1. Return.

                1. If any of the following conditions are unsatisfied:

                    <div class=validusage>
                        - |this|.{{GPUBuffer/[[internal state]]}} is "[=GPUBuffer/[[internal state]]/available=]".
                        - |offset| is a multiple of 8.
                        - |rangeSize| is a multiple of 4.
                        - |offset| + |rangeSize| &le; |this|.{{GPUBuffer/size}}
                        - |mode| contains only bits defined in {{GPUMapMode}}.
                        - |mode| contains exactly one of {{GPUMapMode/READ}} or {{GPUMapMode/WRITE}}.
                        - If |mode| contains {{GPUMapMode/READ}} then |this|.{{GPUBuffer/usage}} must contain {{GPUBufferUsage/MAP_READ}}.
                        - If |mode| contains {{GPUMapMode/WRITE}} then |this|.{{GPUBuffer/usage}} must contain {{GPUBufferUsage/MAP_WRITE}}.
                    </div>

                    Then:

                    1. Set |deviceLost| to `false`.
                    1. Issue the <var data-timeline=content>map failure steps</var> on
                        <var data-timeline=content>contentTimeline</var>.
                    1. [$Generate a validation error$].
                    1. Return.

                1. Set |this|.{{GPUBuffer/[[internal state]]}} to "[=GPUBuffer/[[internal state]]/unavailable=]".

                    Note: Since the buffer is mapped, its contents cannot change between this step and {{GPUBuffer/unmap()}}.

                1. When either of the following events occur (whichever comes first),
                    or if either has already occurred:

                    - The [=device timeline=] becomes informed of the completion of an unspecified
                        [=queue timeline=] point:
                        - after the completion of
                            <span data-timeline=queue>currently-enqueued operations that use |this|</span>
                        - and no later than the completion of
                            <span data-timeline=queue>all currently-enqueued operations</span>
                            (regardless of whether they use |this|).
                    - |this|.{{GPUObjectBase/[[device]]}} [=becomes lost=].

                    Then issue the subsequent steps on the [=device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Set |deviceLost| to `true` if |this|.{{GPUObjectBase/[[device]]}} is [$invalid|lost$],
                    and `false` otherwise.

                    Note: The device could have been lost between the previous block of steps and this one.
                1. If |deviceLost|:

                    1. Issue the <var data-timeline=content>map failure steps</var> on
                        <var data-timeline=content>contentTimeline</var>.

                    Otherwise:

                    1. Let |internalStateAtCompletion| be |this|.{{GPUBuffer/[[internal state]]}}.

                        Note: If, and only if, at this point the buffer has become "[=GPUBuffer/[[internal state]]/available=]"
                        again due to an {{GPUBuffer/unmap()}} call, then {{GPUBuffer/[[pending_map]]}} != |p| below,
                        so mapping will not succeed in the steps below.
                    1. Let |dataForMappedRegion| be the contents of |this| starting at offset |offset|, for |rangeSize| bytes.
                    1. Issue the <var data-timeline=content>map success steps</var> on the
                        <var data-timeline=content>contentTimeline</var>.

                    <!-- POSTV1(multi-queue): this may be better described using queue-transfer language. -->
            </div>
            <div data-timeline=content>
                [=Content timeline=] <var data-timeline=content>map success steps</var>:

                1. If |this|.{{GPUBuffer/[[pending_map]]}} != |p|:

                    Note: The map has been cancelled by {{GPUBuffer/unmap()}}.

                    1. [=Assert=] |p| is rejected.
                    1. Return.
                1. [=Assert=] |p| is pending.
                1. [=Assert=] |internalStateAtCompletion| is "[=GPUBuffer/[[internal state]]/unavailable=]".
                1. Let |mapping| be [$initialize an active buffer mapping$]
                    with mode |mode| and range <code>[|offset|, |offset| + |rangeSize|]</code>.

                    If this allocation fails:

                    1. Set |this|.{{GPUBuffer/[[pending_map]]}} to `null`,
                        and [=reject=] |p| with a {{RangeError}}.
                    1. Return.
                1. Set the content of |mapping|.[=active buffer mapping/data=] to |dataForMappedRegion|.
                1. Set |this|.{{GPUBuffer/[[mapping]]}} to |mapping|.
                1. Set |this|.{{GPUBuffer/[[pending_map]]}} to `null`,
                    and [=resolve=] |p|.
            </div>
            <div data-timeline=content>
                [=Content timeline=] <var data-timeline=content>map failure steps</var>:

                1. If |this|.{{GPUBuffer/[[pending_map]]}} != |p|:

                    Note: The map has been cancelled by {{GPUBuffer/unmap()}}.

                    1. [=Assert=] |p| is already rejected.
                    1. Return.
                1. [=Assert=] |p| is still pending.
                1. Set |this|.{{GPUBuffer/[[pending_map]]}} to `null`.
                1. If |deviceLost|:

                    1. [=Reject=] |p| with an {{AbortError}}.

                        Note: This is the same error type produced by cancelling the map using
                        {{GPUBuffer/unmap()}}.

                    Otherwise:

                    1. [=Reject=] |p| with an {{OperationError}}.
            </div>
        </div>

    : <dfn>getMappedRange(offset, size)</dfn>
    ::
        Returns an {{ArrayBuffer}} with the contents of the {{GPUBuffer}} in the given mapped range.

        <div algorithm=GPUBuffer.getMappedRange>
            <div data-timeline=content>
                **Called on:** {{GPUBuffer}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUBuffer/getMappedRange(offset, size)">
                    |offset|: Offset in bytes into the buffer to return buffer contents from.
                    |size|: Size in bytes of the {{ArrayBuffer}} to return.
                </pre>

                **Returns:** {{ArrayBuffer}}

                [=Content timeline=] steps:

                1. If |size| is missing:
                    1. Let |rangeSize| be max(0, |this|.{{GPUBuffer/size}} - |offset|).

                    Otherwise, let |rangeSize| be |size|.

                1. If any of the following conditions are unsatisfied, throw an {{OperationError}} and return.

                    <div class=validusage>
                        - |this|.{{GPUBuffer/[[mapping]]}} is not `null`.
                        - |offset| is a multiple of 8.
                        - |rangeSize| is a multiple of 4.
                        - |offset| &ge; |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/range=][0].
                        - |offset| + |rangeSize| &le; |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/range=][1].
                        - [|offset|, |offset| + |rangeSize|) does not overlap another range in
                            |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/views=].

                        Note: It is always valid to get mapped ranges of a {{GPUBuffer}} that is
                        {{GPUBufferDescriptor/mappedAtCreation}}, even if it is [$invalid$], because
                        the [=Content timeline=] might not know it is invalid.
                    </div>

                1. Let |data| be |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/data=].

                1. Let |view| be [=!=] [=ArrayBuffer/create|create an ArrayBuffer=] of size |rangeSize|,
                    but with its pointer mutably referencing the content of |data| at offset
                    (|offset| - {{GPUBuffer/[[mapping]]}}.[=active buffer mapping/range=][0]).

                    Note: A {{RangeError}} may not be thrown here, because the |data| has already
                    been allocated during {{GPUBuffer/mapAsync()}} or {{GPUDevice/createBuffer()}}.

                1. Set |view|.{{ArrayBuffer/[[ArrayBufferDetachKey]]}} to "WebGPUBufferMapping".

                    Note: This causes a {{TypeError}} to be thrown if an attempt is made to
                    [$DetachArrayBuffer$], except by {{GPUBuffer/unmap()}}.

                1. [=list/Append=] |view| to |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/views=].

                1. Return |view|.

                Note: User agents should consider issuing a developer-visible warning if
                {{GPUBuffer/getMappedRange()}} succeeds without having checked the status of
                the map, by waiting for {{GPUBuffer/mapAsync()}} to succeed, querying a
                {{GPUBuffer/mapState}} of {{GPUBufferMapState/"mapped"}}, or waiting for a
                later {{GPUQueue/onSubmittedWorkDone()}} call to succeed.
            </div>
        </div>

    : <dfn>unmap()</dfn>
    ::
        Unmaps the mapped range of the {{GPUBuffer}} and makes its contents available for use by the
        GPU again.

        <div algorithm=GPUBuffer.unmap>
            <div data-timeline=content>
                **Called on:** {{GPUBuffer}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. If |this|.{{GPUBuffer/[[pending_map]]}} is not `null`:
                    1. [=Reject=] |this|.{{GPUBuffer/[[pending_map]]}} with an {{AbortError}}.
                    1. Set |this|.{{GPUBuffer/[[pending_map]]}} to `null`.

                1. If |this|.{{GPUBuffer/[[mapping]]}} is `null`:
                    1. Return.

                1. For each {{ArrayBuffer}} |ab| in |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/views=]:
                    1. Perform [$DetachArrayBuffer$](|ab|, "WebGPUBufferMapping").

                1. Let |bufferUpdate| be `null`.

                1. If |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/mode=] contains {{GPUMapMode/WRITE}}:
                    1. Set |bufferUpdate| to {
                        `data`: |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/data=],
                        `offset`: |this|.{{GPUBuffer/[[mapping]]}}.[=active buffer mapping/range=][0]
                        }.

                    Note: When a buffer is mapped without the {{GPUMapMode/WRITE}} mode, then
                    unmapped, any local modifications done by the application to the mapped ranges
                    {{ArrayBuffer}} are discarded and will not affect the content of later mappings.

                1. Set |this|.{{GPUBuffer/[[mapping]]}} to `null`.

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If any of the following conditions are unsatisfied, return.

                    <div class=validusage>
                        - |this| is [$valid to use with$] |this|.{{GPUObjectBase/[[device]]}}.
                    </div>

                1. [=Assert=] |this|.{{GPUBuffer/[[internal state]]}} is "[=GPUBuffer/[[internal state]]/unavailable=]".
                1. If |bufferUpdate| is not `null`:

                    1. Issue the following steps on the [=Queue timeline=] of |this|.{{GPUObjectBase/[[device]]}}.{{GPUDevice/queue}}:

                        <div data-timeline=queue>
                            [=Queue timeline=] steps:

                            1. Update the contents of |this| at offset |bufferUpdate|.`offset`
                                with the data |bufferUpdate|.`data`.
                        </div>
                1. Set |this|.{{GPUBuffer/[[internal state]]}} to "[=GPUBuffer/[[internal state]]/available=]".
            </div>
        </div>
</dl>


# Textures and Texture Views # {#textures}

<h3 id=gputexture data-dfn-type=interface>`GPUTexture`
<span id=texture-interface></span>
</h3>

A <dfn dfn>texture</dfn> is made up of {{GPUTextureDimension/1d}}, {{GPUTextureDimension/2d}},
or {{GPUTextureDimension/3d}} arrays of data which can contain multiple values per-element to
represent things like colors. Textures can be read and written in many ways, depending on the
{{GPUTextureUsage}} they are created with. For example, textures can be sampled, read, and written
from render and compute pipeline shaders, and they can be written by render pass outputs.
Internally, textures are often stored in GPU memory with a layout optimized for
multidimensional access rather than linear access.

One [=texture=] consists of one or more <dfn dfn>texture subresources</dfn>,
each uniquely identified by a [=mipmap level=] and,
for {{GPUTextureDimension/2d}} textures only, [=array layer=] and [=aspect=].

A [=texture subresource=] is a [=subresource=]: each can be used in different
[=internal usages=] within a single [=usage scope=].

Each subresource in a <dfn dfn>mipmap level</dfn> is approximately half the size,
in each spatial dimension, of the corresponding resource in the lesser level
(see [=logical miplevel-specific texture extent=]).
The subresource in level 0 has the dimensions of the texture itself.
Smaller levels are typically used to store lower resolution versions of the same image.
{{GPUSampler}} and WGSL provide facilities for selecting and interpolating between [=levels of
detail=], explicitly or automatically.

A {{GPUTextureDimension/"2d"}} texture may be an array of <dfn dfn>array layer</dfn>s.
Each subresource in a layer is the same size as the corresponding resources in other layers.
For non-2d textures, all subresources have an array layer index of 0.

Each subresource has an <dfn dfn>aspect</dfn>.
Color textures have just one aspect: <dfn dfn for=aspect>color</dfn>.
[=Depth-or-stencil format=] textures may have multiple aspects:
a <dfn dfn for=aspect>depth</dfn> aspect,
a <dfn dfn for=aspect>stencil</dfn> aspect, or both, and may be used in special ways, such as in
{{GPURenderPassDescriptor/depthStencilAttachment}} and in {{GPUTextureSampleType/"depth"}} bindings.

A {{GPUTextureDimension/"3d"}} texture may have multiple <dfn dfn>slice</dfn>s, each being the
two-dimensional image at a particular `z` value in the texture.
Slices are not separate subresources.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUTexture {
    GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});

    undefined destroy();

    readonly attribute GPUIntegerCoordinateOut width;
    readonly attribute GPUIntegerCoordinateOut height;
    readonly attribute GPUIntegerCoordinateOut depthOrArrayLayers;
    readonly attribute GPUIntegerCoordinateOut mipLevelCount;
    readonly attribute GPUSize32Out sampleCount;
    readonly attribute GPUTextureDimension dimension;
    readonly attribute GPUTextureFormat format;
    readonly attribute GPUFlagsConstant usage;
};
GPUTexture includes GPUObjectBase;
</script>

{{GPUTexture}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUTexture data-timeline=const>
    : <dfn>width</dfn>
    ::
        The width of this {{GPUTexture}}.

    : <dfn>height</dfn>
    ::
        The height of this {{GPUTexture}}.

    : <dfn>depthOrArrayLayers</dfn>
    ::
        The depth or layer count of this {{GPUTexture}}.

    : <dfn>mipLevelCount</dfn>
    ::
        The number of mip levels of this {{GPUTexture}}.

    : <dfn>sampleCount</dfn>
    ::
        The number of sample count of this {{GPUTexture}}.

    : <dfn>dimension</dfn>
    ::
        The dimension of the set of texel for each of this {{GPUTexture}}'s subresources.

    : <dfn>format</dfn>
    ::
        The format of this {{GPUTexture}}.

    : <dfn>usage</dfn>
    ::
        The allowed usages for this {{GPUTexture}}.

    : <dfn>\[[viewFormats]]</dfn>, of type [=sequence=]&lt;{{GPUTextureFormat}}&gt;
    ::
        The set of {{GPUTextureFormat}}s that can be used as the
        {{GPUTextureViewDescriptor}}.{{GPUTextureViewDescriptor/format}}
        when creating views on this {{GPUTexture}}.
</dl>

{{GPUTexture}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUTexture data-timeline=device>
    : <dfn>\[[destroyed]]</dfn>, of type {{boolean}}, initially `false`
    ::
        If the texture is destroyed, it can no longer be used in any operation,
        and its underlying memory can be freed.
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>compute render extent</dfn>(baseSize, mipLevel)

    **Arguments:**

    - {{GPUExtent3D}} |baseSize|
    - {{GPUSize32}} |mipLevel|

    **Returns:** {{GPUExtent3DDict}}

    [=Device timeline=] steps:

    1. Let |extent| be a new {{GPUExtent3DDict}} object.
    1. Set |extent|.{{GPUExtent3DDict/width}} to max(1, |baseSize|.[=GPUExtent3D/width=] &Gt; |mipLevel|).
    1. Set |extent|.{{GPUExtent3DDict/height}} to max(1, |baseSize|.[=GPUExtent3D/height=] &Gt; |mipLevel|).
    1. Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to 1.
    1. Return |extent|.
</div>

The <dfn dfn>logical miplevel-specific texture extent</dfn> of a [=texture=] is the size of the
[=texture=] in texels at a specific miplevel. It is calculated by this procedure:

<div algorithm data-timeline=const>
    <dfn abstract-op>Logical miplevel-specific texture extent</dfn>(descriptor, mipLevel)

    **Arguments:**

    - {{GPUTextureDescriptor}} |descriptor|
    - {{GPUSize32}} |mipLevel|

    **Returns:** {{GPUExtent3DDict}}

    1. Let |extent| be a new {{GPUExtent3DDict}} object.
    1. If |descriptor|.{{GPUTextureDescriptor/dimension}} is:

        <dl class=switch>
            : {{GPUTextureDimension/"1d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] &Gt; |mipLevel|).
                - Set |extent|.{{GPUExtent3DDict/height}} to 1.
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to 1.

            : {{GPUTextureDimension/"2d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] &Gt; |mipLevel|).
                - Set |extent|.{{GPUExtent3DDict/height}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] &Gt; |mipLevel|).
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=].

            : {{GPUTextureDimension/"3d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] &Gt; |mipLevel|).
                - Set |extent|.{{GPUExtent3DDict/height}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] &Gt; |mipLevel|).
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to max(1, |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] &Gt; |mipLevel|).
        </dl>
    1. Return |extent|.
</div>

The <dfn dfn>physical miplevel-specific texture extent</dfn> of a [=texture=] is the size of the
[=texture=] in texels at a specific miplevel that includes the possible extra padding
to form complete [=texel blocks=] in the [=texture=]. It is calculated by this procedure:

<div algorithm data-timeline=const>
    <dfn abstract-op>Physical miplevel-specific texture extent</dfn>(descriptor, mipLevel)

    **Arguments:**

    - {{GPUTextureDescriptor}} |descriptor|
    - {{GPUSize32}} |mipLevel|

    **Returns:** {{GPUExtent3DDict}}

    1. Let |extent| be a new {{GPUExtent3DDict}} object.
    1. Let |logicalExtent| be [=logical miplevel-specific texture extent=](|descriptor|, |mipLevel|).
    1. If |descriptor|.{{GPUTextureDescriptor/dimension}} is:

        <dl class=switch>
            : {{GPUTextureDimension/"1d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to |logicalExtent|.[=GPUExtent3D/width=] rounded up to the nearest multiple of |descriptor|'s [=texel block width=].
                - Set |extent|.{{GPUExtent3DDict/height}} to 1.
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to 1.

            : {{GPUTextureDimension/"2d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to |logicalExtent|.[=GPUExtent3D/width=] rounded up to the nearest multiple of |descriptor|'s [=texel block width=].
                - Set |extent|.{{GPUExtent3DDict/height}} to |logicalExtent|.[=GPUExtent3D/height=] rounded up to the nearest multiple of |descriptor|'s [=texel block height=].
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to |logicalExtent|.[=GPUExtent3D/depthOrArrayLayers=].

            : {{GPUTextureDimension/"3d"}}
            ::
                - Set |extent|.{{GPUExtent3DDict/width}} to |logicalExtent|.[=GPUExtent3D/width=] rounded up to the nearest multiple of |descriptor|'s [=texel block width=].
                - Set |extent|.{{GPUExtent3DDict/height}} to |logicalExtent|.[=GPUExtent3D/height=] rounded up to the nearest multiple of |descriptor|'s [=texel block height=].
                - Set |extent|.{{GPUExtent3DDict/depthOrArrayLayers}} to |logicalExtent|.[=GPUExtent3D/depthOrArrayLayers=].
        </dl>
    1. Return |extent|.
</div>

<h4 id=gputexturedescriptor data-dfn-type=dictionary>`GPUTextureDescriptor`
<span id=GPUTextureDescriptor></span>
<span id=dictdef-gputexturedescriptor></span>
</h4>

<script type=idl>
dictionary GPUTextureDescriptor
         : GPUObjectDescriptorBase {
    required GPUExtent3D size;
    GPUIntegerCoordinate mipLevelCount = 1;
    GPUSize32 sampleCount = 1;
    GPUTextureDimension dimension = "2d";
    required GPUTextureFormat format;
    required GPUTextureUsageFlags usage;
    sequence<GPUTextureFormat> viewFormats = [];
};
</script>

{{GPUTextureDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUTextureDescriptor>
    : <dfn>size</dfn>
    ::
        The width, height, and depth or layer count of the texture.

    : <dfn>mipLevelCount</dfn>
    ::
        The number of mip levels the texture will contain.

    : <dfn>sampleCount</dfn>
    ::
        The sample count of the texture. A {{GPUTextureDescriptor/sampleCount}} &gt; `1` indicates
        a multisampled texture.

    : <dfn>dimension</dfn>
    ::
        Whether the texture is one-dimensional, an array of two-dimensional layers, or three-dimensional.

    : <dfn>format</dfn>
    ::
        The format of the texture.

    : <dfn>usage</dfn>
    ::
        The allowed usages for the texture.

    : <dfn>viewFormats</dfn>
    ::
        Specifies what view {{GPUTextureViewDescriptor/format}} values will be allowed when calling
        {{GPUTexture/createView()}} on this texture (in addition to the texture's actual
        {{GPUTextureDescriptor/format}}).

        <div class=note heading>
            Adding a format to this list may have a significant performance impact, so it is best
            to avoid adding formats unnecessarily.

            The actual performance impact is highly dependent on the target system; developers must
            test various systems to find out the impact on their particular application.
            For example, on some systems any texture with a {{GPUTextureDescriptor/format}} or
            {{GPUTextureDescriptor/viewFormats}} entry including
            {{GPUTextureFormat/"rgba8unorm-srgb"}} will perform less optimally than a
            {{GPUTextureFormat/"rgba8unorm"}} texture which does not.
            Similar caveats exist for other formats and pairs of formats on other systems.
        </div>

        Formats in this list must be [=texture view format compatible=] with the texture format.

        <div algorithm data-timeline=const>
            Two {{GPUTextureFormat}}s |format| and |viewFormat| are <dfn dfn for="">texture view format compatible</dfn> if:

            - |format| equals |viewFormat|, or
            - |format| and |viewFormat| differ only in whether they are `srgb` formats (have the `-srgb` suffix).
        </div>
</dl>

<script type=idl>
enum GPUTextureDimension {
    "1d",
    "2d",
    "3d",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUTextureDimension>
    : <dfn>"1d"</dfn>
    ::
        Specifies a texture that has one dimension, width. {{GPUTextureDimension/"1d"}} textures
        cannot have mipmaps, be multisampled, use compressed or depth/stencil formats, or be used as
        a render target.

    : <dfn>"2d"</dfn>
    ::
        Specifies a texture that has a width and height, and may have layers.

    : <dfn>"3d"</dfn>
    ::
        Specifies a texture that has a width, height, and depth. {{GPUTextureDimension/"3d"}}
        textures cannot be multisampled, and their format must support 3d textures
        (all [[#plain-color-formats|plain color formats]] and some [[#packed-formats|packed/compressed formats]]).
</dl>

### Texture Usages ### {#texture-usage}

<script type=idl>
typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
[Exposed=(Window, Worker), SecureContext]
namespace GPUTextureUsage {
    const GPUFlagsConstant COPY_SRC          = 0x01;
    const GPUFlagsConstant COPY_DST          = 0x02;
    const GPUFlagsConstant TEXTURE_BINDING   = 0x04;
    const GPUFlagsConstant STORAGE_BINDING   = 0x08;
    const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
};
</script>

The {{GPUTextureUsage}} flags determine how a {{GPUTexture}} may be used after its creation:

<dl dfn-type=const dfn-for=GPUTextureUsage>
    : <dfn>COPY_SRC</dfn>
    ::
        The texture can be used as the source of a copy operation. (Examples: as the `source`
        argument of a {{GPUCommandEncoder/copyTextureToTexture()}} or
        {{GPUCommandEncoder/copyTextureToBuffer()}} call.)

    : <dfn>COPY_DST</dfn>
    ::
        The texture can be used as the destination of a copy or write operation. (Examples: as the
        `destination` argument of a {{GPUCommandEncoder/copyTextureToTexture()}} or
        {{GPUCommandEncoder/copyBufferToTexture()}} call, or as the target of a
        {{GPUQueue/writeTexture()}} call.)

    : <dfn>TEXTURE_BINDING</dfn>
    ::
        The texture can be bound for use as a sampled texture in a shader (Example: as a bind group
        entry for a {{GPUTextureBindingLayout}}.)

    : <dfn>STORAGE_BINDING</dfn>
    ::
        The texture can be bound for use as a storage texture in a shader (Example: as a bind group
        entry for a {{GPUStorageTextureBindingLayout}}.)

    : <dfn>RENDER_ATTACHMENT</dfn>
    ::
        The texture can be used as a color or depth/stencil attachment in a render pass.
        (Example: as a {{GPURenderPassColorAttachment}}.{{GPURenderPassColorAttachment/view}} or
        {{GPURenderPassDepthStencilAttachment}}.{{GPURenderPassDepthStencilAttachment/view}}.)
</dl>

<div algorithm data-timeline=const>
    <dfn abstract-op>maximum mipLevel count</dfn>(|dimension|, |size|)

    **Arguments:**

    - {{GPUTextureDimension}} |dimension|
    - {{GPUTextureDimension}} |size|

    1. Calculate the max dimension value |m|:
        - If |dimension| is:

            <dl class=switch>
                : {{GPUTextureDimension/"1d"}}
                :: Return 1.

                : {{GPUTextureDimension/"2d"}}
                :: Let |m| = max(|size|.[=GPUExtent3D/width=], |size|.[=GPUExtent3D/height=]).

                : {{GPUTextureDimension/"3d"}}
                :: Let |m| = max(max(|size|.[=GPUExtent3D/width=], |size|.[=GPUExtent3D/height=]), |size|.[=GPUExtent3D/depthOrArrayLayers=]).
            </dl>
    1. Return floor(log<sub>2</sub>(|m|)) + 1.
</div>

### Texture Creation ### {#texture-creation}

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createTexture(descriptor)</dfn>
    ::
        Creates a {{GPUTexture}}.

        <div algorithm=GPUDevice.createTexture>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createTexture(descriptor)">
                    |descriptor|: Description of the {{GPUTexture}} to create.
                </pre>

                **Returns:** {{GPUTexture}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUExtent3D shape$](|descriptor|.{{GPUTextureDescriptor/size}}).
                1. [=?=] [$Validate texture format required features$] of
                    |descriptor|.{{GPUTextureDescriptor/format}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. [=?=] [$Validate texture format required features$] of each element of
                    |descriptor|.{{GPUTextureDescriptor/viewFormats}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. Let |t| be [=!=] [$create a new WebGPU object$](|this|, {{GPUTexture}}, |descriptor|).
                1. Set |t|.{{GPUTexture/width}} to |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=].
                1. Set |t|.{{GPUTexture/height}} to |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=].
                1. Set |t|.{{GPUTexture/depthOrArrayLayers}} to |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=].
                1. Set |t|.{{GPUTexture/mipLevelCount}} to |descriptor|.{{GPUTextureDescriptor/mipLevelCount}}.
                1. Set |t|.{{GPUTexture/sampleCount}} to |descriptor|.{{GPUTextureDescriptor/sampleCount}}.
                1. Set |t|.{{GPUTexture/dimension}} to |descriptor|.{{GPUTextureDescriptor/dimension}}.
                1. Set |t|.{{GPUTexture/format}} to |descriptor|.{{GPUTextureDescriptor/format}}.
                1. Set |t|.{{GPUTexture/usage}} to |descriptor|.{{GPUTextureDescriptor/usage}}.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |t|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |t| and return.

                    <div class=validusage>
                        - [$validating GPUTextureDescriptor$](|this|, |descriptor|) returns `true`.
                    </div>

                1. Set |t|.{{GPUTexture/[[viewFormats]]}} to |descriptor|.{{GPUTextureDescriptor/viewFormats}}.

                1. Create a device allocation for |t| where each block has an
                    [=equivalent texel representation=] to a block with a bit representation of zero.

                    If the allocation fails without side-effects,
                    [$generate an out-of-memory error$], [$invalidate$] |t|, and return.
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUTextureDescriptor</dfn>(|this|, |descriptor|):

    **Arguments:**

    - {{GPUDevice}} |this|
    - {{GPUTextureDescriptor}} |descriptor|

    [=Device timeline=] steps:

    1. Let |limits| be |this|.{{device/[[limits]]}}.
    1. Return `true` if all of the following requirements are met, and `false` otherwise:

        <div class=validusage>
            - |this| must not be [$invalid|lost$].
            - |descriptor|.{{GPUTextureDescriptor/usage}} must not be 0.
            - |descriptor|.{{GPUTextureDescriptor/usage}} must contain only bits present in |this|'s [=allowed texture usages=].
            - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=],
                |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=],
                and |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] must be &gt; zero.
            - |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be &gt; zero.
            - |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be either 1 or 4.
            - If |descriptor|.{{GPUTextureDescriptor/dimension}} is:

                <dl class=switch>
                    : {{GPUTextureDimension/"1d"}}
                    ::
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension1D}}.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] must be 1.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] must be 1.
                        - |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be 1.
                        - |descriptor|.{{GPUTextureDescriptor/format}} must not be a [=compressed format=] or [=depth-or-stencil format=].

                    : {{GPUTextureDimension/"2d"}}
                    ::
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension2D}}.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension2D}}.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] must be &le;
                            |limits|.{{supported limits/maxTextureArrayLayers}}.

                    : {{GPUTextureDimension/"3d"}}
                    ::
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension3D}}.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension3D}}.
                        - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] must be &le;
                            |limits|.{{supported limits/maxTextureDimension3D}}.
                        - |descriptor|.{{GPUTextureDescriptor/sampleCount}} must be 1.
                        - |descriptor|.{{GPUTextureDescriptor/format}} must support {{GPUTextureDimension/"3d"}}
                            textures according to [[#texture-format-caps]].
                </dl>
            - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/width=] must be multiple of [=texel block width=].
            - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/height=] must be multiple of [=texel block height=].
            - If |descriptor|.{{GPUTextureDescriptor/sampleCount}} > 1:
                - |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be 1.
                - |descriptor|.{{GPUTextureDescriptor/size}}.[=GPUExtent3D/depthOrArrayLayers=] must be 1.
                - |descriptor|.{{GPUTextureDescriptor/usage}} must not include the {{GPUTextureUsage/STORAGE_BINDING}} bit.
                - |descriptor|.{{GPUTextureDescriptor/usage}} must include the {{GPUTextureUsage/RENDER_ATTACHMENT}} bit.
                - |descriptor|.{{GPUTextureDescriptor/format}} must support multisampling according to [[#texture-format-caps]].
            - |descriptor|.{{GPUTextureDescriptor/mipLevelCount}} must be &le;
                [$maximum mipLevel count$](|descriptor|.{{GPUTextureDescriptor/dimension}}, |descriptor|.{{GPUTextureDescriptor/size}}).
            - If |descriptor|.{{GPUTextureDescriptor/usage}} includes the {{GPUTextureUsage/RENDER_ATTACHMENT}} bit:
                - |descriptor|.{{GPUTextureDescriptor/format}} must be a [=renderable format=].
                - |descriptor|.{{GPUTextureDescriptor/dimension}} must be either {{GPUTextureDimension/"2d"}} or {{GPUTextureDimension/"3d"}}.
            - If |descriptor|.{{GPUTextureDescriptor/usage}} includes the {{GPUTextureUsage/STORAGE_BINDING}} bit:
                - |descriptor|.{{GPUTextureDescriptor/format}} must be listed in [[#plain-color-formats]] table
                    with {{GPUTextureUsage/STORAGE_BINDING}} capability for at least one access mode.
            - For each |viewFormat| in |descriptor|.{{GPUTextureDescriptor/viewFormats}},
                |descriptor|.{{GPUTextureDescriptor/format}} and |viewFormat| must be
                [=texture view format compatible=].

                <div class=note heading>
                    Implementations may consider issuing a developer-visible warning if |viewFormat| is not compatible with any of the
                    given {{GPUTextureDescriptor/usage}} bits, as that |viewFormat| will be unusable.
                </div>
        </div>
</div>


<div class=example>
    Creating a 16x16, RGBA, 2D texture with one array layer and one mip level:

    <pre highlight=js>
        const texture = gpuDevice.createTexture({
            size: { width: 16, height: 16 },
            format: 'rgba8unorm',
            usage: GPUTextureUsage.TEXTURE_BINDING,
        });
    </pre>
</div>

### Texture Destruction ### {#texture-destruction}

An application that no longer requires a {{GPUTexture}} can choose to lose access to it before
garbage collection by calling {{GPUTexture/destroy()}}.

Note: This allows the user agent to reclaim the GPU memory associated with the {{GPUTexture}} once
all previously submitted operations using it are complete.

{{GPUTexture}} has the following methods:

<dl dfn-type=method dfn-for=GPUTexture>
    : <dfn>destroy()</dfn>
    ::
        Destroys the {{GPUTexture}}.

        <div algorithm=GPUTexture.destroy>
            <div data-timeline=content>
                **Called on:** {{GPUTexture}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=device timeline=].
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Set |this|.{{GPUTexture/[[destroyed]]}} to true.
            </div>
        </div>
</dl>

<h3 id=gputextureview data-dfn-type=interface>`GPUTextureView`
<span id=gpu-textureview></span>
</h3>

A {{GPUTextureView}} is a view onto some subset of the [=texture subresources=] defined by
a particular {{GPUTexture}}.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUTextureView {
};
GPUTextureView includes GPUObjectBase;
</script>

{{GPUTextureView}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUTextureView data-timeline=const>
    : <dfn>\[[texture]]</dfn>, readonly
    ::
        The {{GPUTexture}} into which this is a view.

    : <dfn>\[[descriptor]]</dfn>, readonly
    ::
        The {{GPUTextureViewDescriptor}} describing this texture view.

        All optional fields of {{GPUTextureViewDescriptor}} are defined.

    : <dfn>\[[renderExtent]]</dfn>, readonly
    ::
        For renderable views, this is the effective {{GPUExtent3DDict}} for rendering.

        Note: this extent depends on the {{GPUTextureViewDescriptor/baseMipLevel}}.
</dl>

<div algorithm="texture view subresources">
    The set of <dfn dfn for=GPUTextureView>subresources</dfn> of a texture view |view|,
    with {{GPUTextureView/[[descriptor]]}} |desc|,
    is the subset of the subresources of |view|.{{GPUTextureView/[[texture]]}}
    for which each subresource |s| satisfies the following:

    - The [=mipmap level=] of |s| is &ge;
        |desc|.{{GPUTextureViewDescriptor/baseMipLevel}} and &lt;
        |desc|.{{GPUTextureViewDescriptor/baseMipLevel}} +
        |desc|.{{GPUTextureViewDescriptor/mipLevelCount}}.
    - The [=array layer=] of |s| is &ge;
        |desc|.{{GPUTextureViewDescriptor/baseArrayLayer}} and &lt;
        |desc|.{{GPUTextureViewDescriptor/baseArrayLayer}} +
        |desc|.{{GPUTextureViewDescriptor/arrayLayerCount}}.
    - The [=aspect=] of |s| is in the [=GPUTextureAspect/set of aspects=] of
        |desc|.{{GPUTextureViewDescriptor/aspect}}.

    Two {{GPUTextureView}} objects are <dfn dfn>texture-view-aliasing</dfn> if and only if
    their sets of subresources intersect.
</div>

### Texture View Creation ### {#texture-view-creation}

<script type=idl>
dictionary GPUTextureViewDescriptor
         : GPUObjectDescriptorBase {
    GPUTextureFormat format;
    GPUTextureViewDimension dimension;
    GPUTextureUsageFlags usage = 0;
    GPUTextureAspect aspect = "all";
    GPUIntegerCoordinate baseMipLevel = 0;
    GPUIntegerCoordinate mipLevelCount;
    GPUIntegerCoordinate baseArrayLayer = 0;
    GPUIntegerCoordinate arrayLayerCount;
};
</script>

{{GPUTextureViewDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUTextureViewDescriptor>
    : <dfn>format</dfn>
    ::
        The format of the texture view. Must be either the {{GPUTextureDescriptor/format}} of the
        texture or one of the {{GPUTextureDescriptor/viewFormats}} specified during its creation.

    : <dfn>dimension</dfn>
    ::
        The dimension to view the texture as.

    : <dfn>usage</dfn>
    ::
        The allowed {{GPUTextureUsage|usage(s)}} for the texture view. Must be a subset of the
        {{GPUTexture/usage}} flags of the texture. If 0, defaults to the full set of
        {{GPUTexture/usage}} flags of the texture.

        Note: If the view's {{GPUTextureViewDescriptor/format}} doesn't support all of the
        texture's {{GPUTextureDescriptor/usage}}s, the default will fail,
        and the view's {{GPUTextureViewDescriptor/usage}} must be specified explicitly.

    : <dfn>aspect</dfn>
    ::
        Which {{GPUTextureAspect|aspect(s)}} of the texture are accessible to the texture view.

    : <dfn>baseMipLevel</dfn>
    ::
        The first (most detailed) mipmap level accessible to the texture view.

    : <dfn>mipLevelCount</dfn>
    ::
        How many mipmap levels, starting with {{GPUTextureViewDescriptor/baseMipLevel}}, are accessible to
        the texture view.

    : <dfn>baseArrayLayer</dfn>
    ::
        The index of the first array layer accessible to the texture view.

    : <dfn>arrayLayerCount</dfn>
    ::
        How many array layers, starting with {{GPUTextureViewDescriptor/baseArrayLayer}}, are accessible
        to the texture view.
</dl>

<script type=idl>
enum GPUTextureViewDimension {
    "1d",
    "2d",
    "2d-array",
    "cube",
    "cube-array",
    "3d",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUTextureViewDimension>
    : <dfn>"1d"</dfn>
    ::
        The texture is viewed as a 1-dimensional image.

        Corresponding WGSL types:

        - `texture_1d`
        - `texture_storage_1d`

    : <dfn>"2d"</dfn>
    ::
        The texture is viewed as a single 2-dimensional image.

        Corresponding WGSL types:

        - `texture_2d`
        - `texture_storage_2d`
        - `texture_multisampled_2d`
        - `texture_depth_2d`
        - `texture_depth_multisampled_2d`

    : <dfn>"2d-array"</dfn>
    ::
        The texture view is viewed as an array of 2-dimensional images.

        Corresponding WGSL types:

        - `texture_2d_array`
        - `texture_storage_2d_array`
        - `texture_depth_2d_array`

    : <dfn>"cube"</dfn>
    ::
        The texture is viewed as a cubemap.

        The view has 6 array layers, each corresponding to a face of the cube in the order
        `[+X, -X, +Y, -Y, +Z, -Z]` and the following orientations:

        <figure>
            <figcaption>
                Cubemap faces.
                The +U/+V axes indicate the individual faces' texture coordinates,
                and thus the [=texel copy=] memory layout of each face.
            </figcaption>
            <object type="image/svg+xml" data="img/cubemap.svg" width=350 height=260></object>
        </figure>

        Note: When viewed from the inside, this results in a left-handed coordinate system
        where +X is right, +Y is up, and +Z is forward.

        Sampling is done seamlessly across the faces of the cubemap.

        Corresponding WGSL types:

        - `texture_cube`
        - `texture_depth_cube`

    : <dfn>"cube-array"</dfn>
    ::
        The texture is viewed as a packed array of |n| cubemaps,
        each with 6 array layers behaving like one {{GPUTextureViewDimension/"cube"}} view,
        for 6|n| array layers in total.

        Corresponding WGSL types:

        - `texture_cube_array`
        - `texture_depth_cube_array`

    : <dfn>"3d"</dfn>
    ::
        The texture is viewed as a 3-dimensional image.

        Corresponding WGSL types:

        - `texture_3d`
        - `texture_storage_3d`
</dl>

Each <dfn enum>GPUTextureAspect</dfn> value corresponds to a set of [=aspects=].
The <dfn dfn for=GPUTextureAspect>set of aspects</dfn> are defined for each value below.

<script type=idl>
enum GPUTextureAspect {
    "all",
    "stencil-only",
    "depth-only",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUTextureAspect>
    : <dfn>"all"</dfn>
    ::
        All available aspects of the texture format will be accessible to the texture view. For
        color formats the color aspect will be accessible. For
        [=combined depth-stencil format=]s both the depth and stencil aspects will be accessible.
        [=Depth-or-stencil format=]s with a single aspect will only make that aspect accessible.

        The [=GPUTextureAspect/set of aspects=] is [[=aspect/color=], [=aspect/depth=], [=aspect/stencil=]].

    : <dfn>"stencil-only"</dfn>
    ::
        Only the stencil aspect of a [=depth-or-stencil format=] format will be accessible to the
        texture view.

        The [=GPUTextureAspect/set of aspects=] is [[=aspect/stencil=]].

    : <dfn>"depth-only"</dfn>
    ::
        Only the depth aspect of a [=depth-or-stencil format=] format will be accessible to the
        texture view.

        The [=GPUTextureAspect/set of aspects=] is [[=aspect/depth=]].
</dl>

<dl dfn-type=method dfn-for=GPUTexture>
    : <dfn>createView(descriptor)</dfn>
    ::
        Creates a {{GPUTextureView}}.

        <div class=note heading>
            By default {{GPUTexture/createView()}} will create a view with a dimension that can
            represent the entire texture. For example, calling {{GPUTexture/createView()}} without
            specifying a {{GPUTextureViewDescriptor/dimension}} on a {{GPUTextureDimension/"2d"}}
            texture with more than one layer will create a {{GPUTextureViewDimension/"2d-array"}}
            {{GPUTextureView}}, even if an {{GPUTextureViewDescriptor/arrayLayerCount}} of 1 is
            specified.

            For textures created from sources where the layer count is unknown at the
            time of development it is recommended that calls to {{GPUTexture/createView()}} are provided
            with an explicit {{GPUTextureViewDescriptor/dimension}} to ensure shader compatibility.
        </div>

        <div algorithm=GPUTexture.createView>
            <div data-timeline=content>
                **Called on:** {{GPUTexture}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUTexture/createView(descriptor)">
                    |descriptor|: Description of the {{GPUTextureView}} to create.
                </pre>

                **Returns:** |view|, of type {{GPUTextureView}}.

                [=Content timeline=] steps:

                1. [=?=] [$Validate texture format required features$] of
                    |descriptor|.{{GPUTextureViewDescriptor/format}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. Let |view| be [=!=] [$create a new WebGPU object$](|this|, {{GPUTextureView}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |view|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Set |descriptor| to the result of [$resolving GPUTextureViewDescriptor defaults$]
                    for |this| with |descriptor|.
                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |view| and return.

                    <div class=validusage>
                        - |this| is [$valid to use with$] |this|.{{GPUObjectBase/[[device]]}}.
                        - |descriptor|.{{GPUTextureViewDescriptor/aspect}} must be present in |this|.{{GPUTexture/format}}.
                        - If the |descriptor|.{{GPUTextureViewDescriptor/aspect}} is {{GPUTextureAspect/"all"}}:
                            - |descriptor|.{{GPUTextureViewDescriptor/format}} must equal either
                                    |this|.{{GPUTexture/format}} or one
                                    of the formats in |this|.{{GPUTexture/[[viewFormats]]}}.

                            Otherwise:

                            - |descriptor|.{{GPUTextureViewDescriptor/format}} must equal the result of [$resolving GPUTextureAspect$](
                                |this|.{{GPUTexture/format}},
                                |descriptor|.{{GPUTextureViewDescriptor/aspect}}).

                        - |descriptor|.{{GPUTextureViewDescriptor/usage}} must be a subset of |this|.{{GPUTexture/usage}}.
                        - If |descriptor|.{{GPUTextureViewDescriptor/usage}} includes the {{GPUTextureUsage/RENDER_ATTACHMENT}} bit:
                            - |descriptor|.{{GPUTextureViewDescriptor/format}} must be a [=renderable format=].
                        - If |descriptor|.{{GPUTextureViewDescriptor/usage}} includes the {{GPUTextureUsage/STORAGE_BINDING}} bit:
                            - |descriptor|.{{GPUTextureViewDescriptor/format}} must be listed in [[#plain-color-formats]] table
                                with {{GPUTextureUsage/STORAGE_BINDING}} capability for at least one access mode.
                        - |descriptor|.{{GPUTextureViewDescriptor/mipLevelCount}} must be &gt; 0.
                        - |descriptor|.{{GPUTextureViewDescriptor/baseMipLevel}} +
                            |descriptor|.{{GPUTextureViewDescriptor/mipLevelCount}} must be &le;
                            |this|.{{GPUTexture/mipLevelCount}}.
                        - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be &gt; 0.
                        - |descriptor|.{{GPUTextureViewDescriptor/baseArrayLayer}} +
                            |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be &le;
                            the [$array layer count$] of |this|.
                        - If |this|.{{GPUTexture/sampleCount}} &gt; 1,
                            |descriptor|.{{GPUTextureViewDescriptor/dimension}} must be {{GPUTextureViewDimension/"2d"}}.
                        - If |descriptor|.{{GPUTextureViewDescriptor/dimension}} is:

                            <dl class=switch>
                                : {{GPUTextureViewDimension/"1d"}}
                                ::
                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"1d"}}.
                                    - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.

                                : {{GPUTextureViewDimension/"2d"}}
                                ::

                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"2d"}}.
                                    - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.

                                : {{GPUTextureViewDimension/"2d-array"}}
                                ::
                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"2d"}}.

                                : {{GPUTextureViewDimension/"cube"}}
                                ::
                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"2d"}}.
                                    - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `6`.
                                    - |this|.{{GPUTexture/width}} must equal |this|.{{GPUTexture/height}}.

                                : {{GPUTextureViewDimension/"cube-array"}}
                                ::
                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"2d"}}.
                                    - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be a multiple of `6`.
                                    - |this|.{{GPUTexture/width}} must equal |this|.{{GPUTexture/height}}.

                                : {{GPUTextureViewDimension/"3d"}}
                                ::
                                    - |this|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"3d"}}.
                                    - |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be `1`.
                            </dl>
                    </div>

                1. Let |view| be a new {{GPUTextureView}} object.
                1. Set |view|.{{GPUTextureView/[[texture]]}} to |this|.
                1. Set |view|.{{GPUTextureView/[[descriptor]]}} to |descriptor|.
                1. If |descriptor|.{{GPUTextureViewDescriptor/usage}} contains {{GPUTextureUsage/RENDER_ATTACHMENT}}:
                    1. Let |renderExtent| be [$compute render extent$]([|this|.{{GPUTexture/width}}, |this|.{{GPUTexture/height}}, |this|.{{GPUTexture/depthOrArrayLayers}}], |descriptor|.{{GPUTextureViewDescriptor/baseMipLevel}}).
                    1. Set |view|.{{GPUTextureView/[[renderExtent]]}} to |renderExtent|.
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    When <dfn abstract-op>resolving GPUTextureViewDescriptor defaults</dfn> for {{GPUTextureView}}
    |texture| with a {{GPUTextureViewDescriptor}} |descriptor|, run the following [=device timeline=] steps:

    1. Let |resolved| be a copy of |descriptor|.
    1. If |resolved|.{{GPUTextureViewDescriptor/format}} is not [=map/exist|provided=]:
        1. Let |format| be the result of [$resolving GPUTextureAspect$](
                {{GPUTexture/format}},
                |descriptor|.{{GPUTextureViewDescriptor/aspect}}).
        1. If |format| is `null`:

            - Set |resolved|.{{GPUTextureViewDescriptor/format}} to |texture|.{{GPUTexture/format}}.

            Otherwise:

            - Set |resolved|.{{GPUTextureViewDescriptor/format}} to |format|.
    1. If |resolved|.{{GPUTextureViewDescriptor/mipLevelCount}} is not [=map/exist|provided=]:
        set |resolved|.{{GPUTextureViewDescriptor/mipLevelCount}} to |texture|.{{GPUTexture/mipLevelCount}}
        &minus; |resolved|.{{GPUTextureViewDescriptor/baseMipLevel}}.
    1. If |resolved|.{{GPUTextureViewDescriptor/dimension}} is not [=map/exist|provided=] and
        |texture|.{{GPUTexture/dimension}} is:

        <dl class=switch>
            : {{GPUTextureDimension/"1d"}}
            :: Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"1d"}}.

            : {{GPUTextureDimension/"2d"}}
            :: If the [$array layer count$] of |texture| is 1:

                - Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"2d"}}.

                Otherwise:

                - Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"2d-array"}}.

            : {{GPUTextureDimension/"3d"}}
            :: Set |resolved|.{{GPUTextureViewDescriptor/dimension}} to {{GPUTextureViewDimension/"3d"}}.
        </dl>
    1. If |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} is not [=map/exist|provided=] and
        |resolved|.{{GPUTextureViewDescriptor/dimension}} is:

        <dl class=switch>
            : {{GPUTextureViewDimension/"1d"}}, {{GPUTextureViewDimension/"2d"}}, or
                {{GPUTextureViewDimension/"3d"}}
            :: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to `1`.

            : {{GPUTextureViewDimension/"cube"}}
            :: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to `6`.

            : {{GPUTextureViewDimension/"2d-array"}} or {{GPUTextureViewDimension/"cube-array"}}
            :: Set |resolved|.{{GPUTextureViewDescriptor/arrayLayerCount}} to the [$array layer count$] of |texture|
                &minus; |resolved|.{{GPUTextureViewDescriptor/baseArrayLayer}}.
        </dl>
    1. If |resolved|.{{GPUTextureViewDescriptor/usage}} is `0`:
        set |resolved|.{{GPUTextureViewDescriptor/usage}} to |texture|.{{GPUTexture/usage}}.

    1. Return |resolved|.
</div>

<div algorithm data-timeline=const>
    To determine the <dfn abstract-op>array layer count</dfn> of {{GPUTexture}} |texture|, run the
    following steps:

        1. If |texture|.{{GPUTexture/dimension}} is:

            <dl class=switch>
                : {{GPUTextureDimension/"1d"}} or {{GPUTextureDimension/"3d"}}
                :: Return `1`.

                : {{GPUTextureDimension/"2d"}}
                :: Return |texture|.{{GPUTexture/depthOrArrayLayers}}.
            </dl>
</div>

## Texture Formats ## {#texture-formats}

The name of the format specifies the order of components, bits per component,
and data type for the component.

- `r`, `g`, `b`, `a` = red, green, blue, alpha
- `unorm` = unsigned normalized
- `snorm` = signed normalized
- `uint` = unsigned int
- `sint` = signed int
- `float` = floating point

If the format has the `-srgb` suffix, then sRGB conversions from gamma to linear
and vice versa are applied during the reading and writing of color values in the
shader. Compressed texture formats are provided by [=features=]. Their naming
should follow the convention here, with the texture name as a prefix. e.g.
`etc2-rgba8unorm`.

The <dfn dfn>texel block</dfn> is a single addressable element of the textures in pixel-based {{GPUTextureFormat}}s,
and a single compressed block of the textures in block-based compressed {{GPUTextureFormat}}s.

The <dfn dfn>texel block width</dfn> and <dfn dfn>texel block height</dfn> specifies the dimension of one [=texel block=].

- For pixel-based {{GPUTextureFormat}}s, the [=texel block width=] and [=texel block height=] are always 1.
- For block-based compressed {{GPUTextureFormat}}s, the [=texel block width=] is the number of texels in each row of one [=texel block=],
    and the [=texel block height=] is the number of texel rows in one [=texel block=]. See [[#texture-format-caps]] for an exhaustive list
    of values for every texture format.

The <dfn dfn>texel block copy footprint</dfn> of an [=aspect=] of a {{GPUTextureFormat}} is the number of
bytes one texel block occupies during a [=texel copy=], if applicable.

Note:
The <dfn dfn>texel block memory cost</dfn> of a {{GPUTextureFormat}} is the number of
bytes needed to store one [=texel block=]. It is not fully defined for all formats.
**This value is informative and non-normative.**

<script type=idl>
enum GPUTextureFormat {
    // 8-bit formats
    "r8unorm",
    "r8snorm",
    "r8uint",
    "r8sint",

    // 16-bit formats
    "r16unorm",
    "r16snorm",
    "r16uint",
    "r16sint",
    "r16float",
    "rg8unorm",
    "rg8snorm",
    "rg8uint",
    "rg8sint",

    // 32-bit formats
    "r32uint",
    "r32sint",
    "r32float",
    "rg16unorm",
    "rg16snorm",
    "rg16uint",
    "rg16sint",
    "rg16float",
    "rgba8unorm",
    "rgba8unorm-srgb",
    "rgba8snorm",
    "rgba8uint",
    "rgba8sint",
    "bgra8unorm",
    "bgra8unorm-srgb",
    // Packed 32-bit formats
    "rgb9e5ufloat",
    "rgb10a2uint",
    "rgb10a2unorm",
    "rg11b10ufloat",

    // 64-bit formats
    "rg32uint",
    "rg32sint",
    "rg32float",
    "rgba16unorm",
    "rgba16snorm",
    "rgba16uint",
    "rgba16sint",
    "rgba16float",

    // 128-bit formats
    "rgba32uint",
    "rgba32sint",
    "rgba32float",

    // Depth/stencil formats
    "stencil8",
    "depth16unorm",
    "depth24plus",
    "depth24plus-stencil8",
    "depth32float",

    // "depth32float-stencil8" feature
    "depth32float-stencil8",

    // BC compressed formats usable if "texture-compression-bc" is both
    // supported by the device/user agent and enabled in requestDevice.
    "bc1-rgba-unorm",
    "bc1-rgba-unorm-srgb",
    "bc2-rgba-unorm",
    "bc2-rgba-unorm-srgb",
    "bc3-rgba-unorm",
    "bc3-rgba-unorm-srgb",
    "bc4-r-unorm",
    "bc4-r-snorm",
    "bc5-rg-unorm",
    "bc5-rg-snorm",
    "bc6h-rgb-ufloat",
    "bc6h-rgb-float",
    "bc7-rgba-unorm",
    "bc7-rgba-unorm-srgb",

    // ETC2 compressed formats usable if "texture-compression-etc2" is both
    // supported by the device/user agent and enabled in requestDevice.
    "etc2-rgb8unorm",
    "etc2-rgb8unorm-srgb",
    "etc2-rgb8a1unorm",
    "etc2-rgb8a1unorm-srgb",
    "etc2-rgba8unorm",
    "etc2-rgba8unorm-srgb",
    "eac-r11unorm",
    "eac-r11snorm",
    "eac-rg11unorm",
    "eac-rg11snorm",

    // ASTC compressed formats usable if "texture-compression-astc" is both
    // supported by the device/user agent and enabled in requestDevice.
    "astc-4x4-unorm",
    "astc-4x4-unorm-srgb",
    "astc-5x4-unorm",
    "astc-5x4-unorm-srgb",
    "astc-5x5-unorm",
    "astc-5x5-unorm-srgb",
    "astc-6x5-unorm",
    "astc-6x5-unorm-srgb",
    "astc-6x6-unorm",
    "astc-6x6-unorm-srgb",
    "astc-8x5-unorm",
    "astc-8x5-unorm-srgb",
    "astc-8x6-unorm",
    "astc-8x6-unorm-srgb",
    "astc-8x8-unorm",
    "astc-8x8-unorm-srgb",
    "astc-10x5-unorm",
    "astc-10x5-unorm-srgb",
    "astc-10x6-unorm",
    "astc-10x6-unorm-srgb",
    "astc-10x8-unorm",
    "astc-10x8-unorm-srgb",
    "astc-10x10-unorm",
    "astc-10x10-unorm-srgb",
    "astc-12x10-unorm",
    "astc-12x10-unorm-srgb",
    "astc-12x12-unorm",
    "astc-12x12-unorm-srgb",
};
</script>

<p id=depthPlus>
The depth component of the {{GPUTextureFormat/"depth24plus"}} and {{GPUTextureFormat/"depth24plus-stencil8"}}
formats may be implemented as either a [=24-bit depth=] value or a {{GPUTextureFormat/"depth32float"}} value.
</p>

The {{GPUTextureFormat/stencil8}} format may be implemented as
either a real "stencil8", or "depth24stencil8", where the depth aspect is
hidden and inaccessible.

<div class=note heading>
    While the precision of depth32float channels is strictly higher than the precision of
    [=24-bit depth=] channels for all values in the representable range (0.0 to 1.0),
    note that the set of representable values is not an exact superset.

    - For [=24-bit depth=], 1 ULP has a constant value of 1 / (2<sup>24</sup> &minus; 1).
    - For depth32float, 1 ULP has a variable value no greater than 1 / (2<sup>24</sup>).
</div>

A format is <dfn lt="renderable|renderable format">renderable</dfn> if it is either a <dfn>color renderable format</dfn>, or a [=depth-or-stencil format=].
If a format is listed in [[#plain-color-formats]] with {{GPUTextureUsage/RENDER_ATTACHMENT}} capability, it is a
color renderable format. Any other format is not a color renderable format.
All [=depth-or-stencil formats=] are renderable.

A [=renderable format=] is also <dfn lt="blendable|blendable format">blendable</dfn>
if it can be used with render pipeline blending.
See [[#texture-format-caps]].

A format is <dfn lt="filterable|filterable format">filterable</dfn> if it supports the
{{GPUTextureSampleType}} {{GPUTextureSampleType/"float"}}
(not just {{GPUTextureSampleType/"unfilterable-float"}});
that is, it can be used with {{GPUSamplerBindingType/"filtering"}} {{GPUSampler}}s.
See [[#texture-format-caps]].

<div algorithm data-timeline=const>
    <dfn abstract-op>resolving GPUTextureAspect</dfn>(format, aspect)

    **Arguments:**

    - {{GPUTextureFormat}} |format|
    - {{GPUTextureAspect}} |aspect|

    **Returns:** {{GPUTextureFormat}} or `null`

    1. If |aspect| is:

        <dl class=switch>
            : {{GPUTextureAspect/"all"}}
            :: Return |format|.

            : {{GPUTextureAspect/"depth-only"}}
            : {{GPUTextureAspect/"stencil-only"}}
            :: If |format| is a depth-stencil-format:
                Return the [=aspect-specific format=] of |format| according to [[#depth-formats]] or `null` if
                    the aspect is not present in |format|.
        </dl>
    1. Return `null`.
</div>

Use of some texture formats require a feature to be enabled on the {{GPUDevice}}. Because new
formats can be added to the specification, those enum values may not be known by the implementation.
In order to normalize behavior across implementations, attempting to use a format that requires a
feature will throw an exception if the associated feature is not enabled on the device. This makes
the behavior the same as when the format is unknown to the implementation.

See [[#texture-format-caps]] for information about which {{GPUTextureFormat}}s require features.

<div algorithm class=validusage data-timeline=content>
    To <dfn abstract-op>Validate texture format required features</dfn> of a {{GPUTextureFormat}} |format|<br/>
    with logical [=device=] |device|, run the following [=content timeline=] steps:

    1. If |format| requires a feature and |device|.{{device/[[features]]}} does not [=list/contain=]
        the feature:
        1. Throw a {{TypeError}}.
</div>

<h3 id=gpuexternaltexture data-dfn-type=interface>`GPUExternalTexture`
<span id=gpu-external-texture></span>
</h3>

A {{GPUExternalTexture}} is a sampleable 2D texture wrapping an external video frame.
It is an immutable snapshot; its contents may not change over time, either from inside WebGPU
(it is only sampleable) or from outside WebGPU (e.g. due to video frame advancement).

{{GPUExternalTexture}}s can be bound into bind groups via the
{{GPUBindGroupLayoutEntry/externalTexture}} bind group layout entry member.
Note that member uses several binding slots, as defined there.

<div class=note heading>
    {{GPUExternalTexture}} *can* be implemented without creating a copy of the imported source,
    but this depends [=implementation-defined=] factors.
    Ownership of the underlying representation may either be exclusive or shared with other
    owners (such as a video decoder), but this is not visible to the application.

    The underlying representation of an external texture is unobservable
    (except for precise sampling behavior), but typically may include:

    - Up to three 2D planes of data (e.g. RGBA, Y+UV, Y+U+V).
    - Metadata for converting coordinates before reading from those planes (crop and rotation).
    - Metadata for converting values into the specified output color space (matrices, gammas, 3D LUT).

    The configuration used internally by an implementation may not be consistent across time,
    systems, user agents, media sources, or even frames within a single video source.
    In order to account for many possible representations,
    the binding conservatively uses the following, for *each* external texture:

    - three sampled texture bindings (for up to 3 planes),
    - one sampled texture binding for a 3D LUT,
    - one sampler binding to sample the 3D LUT, and
    - one uniform buffer binding for metadata.
</div>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUExternalTexture {
};
GPUExternalTexture includes GPUObjectBase;
</script>

{{GPUExternalTexture}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUExternalTexture data-timeline=const>
    : <dfn>\[[descriptor]]</dfn>, of type {{GPUExternalTextureDescriptor}}, readonly
    ::
        The descriptor with which the texture was created.
</dl>

{{GPUExternalTexture}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUExternalTexture data-timeline=const>
    : <dfn>\[[expired]]</dfn>, of type {{boolean}}, initially `false`
    ::
        Indicates whether the object has expired (can no longer be used).

        Note:
        Unlike `[[destroyed]]` slots, which are similar, this can change from `true` back to `false`.
</dl>

### Importing External Textures ### {#external-texture-creation}

An external texture is created from an external video object
using {{GPUDevice/importExternalTexture()}}.

An external texture created from an {{HTMLVideoElement}} expires (is destroyed) automatically in a
task after it is imported, instead of manually or upon garbage collection like other resources.
When an external texture expires, its {{GPUExternalTexture/[[expired]]}} slot changes to `true`.

An external texture created from a {{VideoFrame}} expires (is destroyed) when, and only when,
the source {{VideoFrame}} is [=Close VideoFrame|closed=],
either explicitly by {{VideoFrame/close()}}, or by other means.

Note: As noted in {{VideoDecoder/decode()}}, authors **should** call
{{VideoFrame/close()}} on output {{VideoFrame}}s to avoid decoder stalls.
If an imported {{VideoFrame}} is dropped without being closed, the imported
{{GPUExternalTexture}} object will keep it alive until it is also dropped.
The {{VideoFrame}} cannot be garbage collected until both objects are dropped.
Garbage collection is unpredictable, so this may still stall the video decoder.

Once the {{GPUExternalTexture}} expires, {{GPUDevice/importExternalTexture()}} must be called again.
However, the user agent may un-expire and return the same {{GPUExternalTexture}} again, instead of
creating a new one. This will commonly happen unless the execution of the application is scheduled
to match the video's frame rate (e.g. using `requestVideoFrameCallback()`).
If the same object is returned again, it will compare equal, and {{GPUBindGroup}}s,
{{GPURenderBundle}}s, etc. referencing the previous object can still be used.

<script type=idl>
dictionary GPUExternalTextureDescriptor
         : GPUObjectDescriptorBase {
    required (HTMLVideoElement or VideoFrame) source;
    PredefinedColorSpace colorSpace = "srgb";
};
</script>

{{GPUExternalTextureDescriptor}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUExternalTextureDescriptor>
    : <dfn>source</dfn>
    ::
        The video source to import the external texture from. Source size is determined as described
        by the [=external source dimensions=] table.

    : <dfn>colorSpace</dfn>
    ::
        The color space the image contents of {{GPUExternalTextureDescriptor/source}} will be
        converted into when reading.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>importExternalTexture(descriptor)</dfn>
    ::
        Creates a {{GPUExternalTexture}} wrapping the provided image source.

        <div algorithm=GPUDevice.importExternalTexture>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/importExternalTexture(descriptor)">
                    |descriptor|: Provides the external image source object (and any creation options).
                </pre>

                **Returns:** {{GPUExternalTexture}}

                [=Content timeline=] steps:

                1. Let |source| be |descriptor|.{{GPUExternalTextureDescriptor/source}}.

                1. If the current image contents of |source| are the same as the most recent
                    {{GPUDevice/importExternalTexture()}} call with the same |descriptor|
                    (ignoring {{GPUObjectDescriptorBase/label}}),
                    and the user agent chooses to reuse it:

                    1. Let |previousResult| be the {{GPUExternalTexture}} returned previously.
                    1. Set |previousResult|.{{GPUExternalTexture/[[expired]]}} to `false`,
                        renewing ownership of the underlying resource.
                    1. Let |result| be |previousResult|.

                    Note:
                    This allows the application to detect duplicate imports and avoid re-creating
                    dependent objects (such as {{GPUBindGroup}}s).
                    Implementations still need to be able to handle a single frame being wrapped by
                    multiple {{GPUExternalTexture}}, since import metadata like
                    {{GPUExternalTextureDescriptor/colorSpace}} can change even for the same frame.

                    Otherwise:

                    1. If |source| <l spec=html>[=is not origin-clean=]</l>,
                        throw a {{SecurityError}} and return.

                    1. Let |usability| be [=?=] [=check the usability of the image argument=](|source|).

                    1. If |usability| is not `good`:
                        1. [$Generate a validation error$].
                        1. Return an [$invalidated$] {{GPUExternalTexture}}.

                    1. Let |data| be the result of converting the current image contents of |source| into
                        the color space |descriptor|.{{GPUExternalTextureDescriptor/colorSpace}}
                        with unpremultiplied alpha.

                        This [[#color-space-conversions|may result]] in values outside of the range [0, 1].
                        If clamping is desired, it may be performed after sampling.

                        Note: This is described like a copy, but may be implemented as a reference to
                        read-only underlying data plus appropriate metadata to perform conversion later.

                    1. Let |result| be a new {{GPUExternalTexture}} object wrapping |data|.

                1. If |source| is an {{HTMLVideoElement}},
                    [$queue an automatic expiry task$] with device |this| and the following steps:

                    <div data-timeline=content>
                        1. Set |result|.{{GPUExternalTexture/[[expired]]}} to `true`,
                            releasing ownership of the underlying resource.
                    </div>

                    Note:
                    An {{HTMLVideoElement}} should be imported in the same task that samples the texture
                    (which should generally be scheduled using `requestVideoFrameCallback` or
                    {{AnimationFrameProvider/requestAnimationFrame()}} depending on the application).
                    Otherwise, a texture could get destroyed by these steps before the
                    application is finished using it.
                1. If |source| is a {{VideoFrame}}, then when |source| is
                    [=Close VideoFrame|closed=], run the following steps:

                    <div data-timeline=content>
                        1. Set |result|.{{GPUExternalTexture/[[expired]]}} to `true`.
                    </div>
                1. Set |result|.{{GPUObjectBase/label}} to |descriptor|.{{GPUObjectDescriptorBase/label}}.
                1. Return |result|.
            </div>
        </div>
</dl>

<div class=example>
    Rendering using an video element external texture at the page animation frame rate:

    <pre highlight=js>
        const videoElement = document.createElement('video');
        // ... set up videoElement, wait for it to be ready...

        function frame() {
            requestAnimationFrame(frame);

            // Always re-import the video on every animation frame, because the
            // import is likely to have expired.
            // The browser may cache and reuse a past frame, and if it does it
            // may return the same GPUExternalTexture object again.
            // In this case, old bind groups are still valid.
            const externalTexture = gpuDevice.importExternalTexture({
                source: videoElement
            });

            // ... render using externalTexture...
        }
        requestAnimationFrame(frame);
    </pre>
</div>

<div class=example>
    Rendering using an video element external texture at the video's frame rate, if
    `requestVideoFrameCallback` is available:

    <pre highlight=js>
        const videoElement = document.createElement('video');
        // ... set up videoElement...

        function frame() {
            videoElement.requestVideoFrameCallback(frame);

            // Always re-import, because we know the video frame has advanced
            const externalTexture = gpuDevice.importExternalTexture({
                source: videoElement
            });

            // ... render using externalTexture...
        }
        videoElement.requestVideoFrameCallback(frame);
    </pre>
</div>

## Sampling External Texture Bindings ## {#external-texture-sampling}

The {{GPUBindGroupLayoutEntry/externalTexture}} binding point allows binding {{GPUExternalTexture}}
objects (from dynamic image sources like videos). It also supports {{GPUTexture}} and {{GPUTextureView}}.

Note:
When a {{GPUTexture}} or a {{GPUTextureView}} is bound to an {{GPUBindGroupLayoutEntry/externalTexture}}
binding, it is like a {{GPUExternalTexture}} with a single RGBA plane and no crop, rotation, or color
conversion.

External textures are represented in WGSL with `texture_external` and may be read using
`textureLoad` and `textureSampleBaseClampToEdge`.

The `sampler` provided to `textureSampleBaseClampToEdge` is used to sample the underlying textures.

When the [=binding resource type=] is a {{GPUExternalTexture}}, the result is in the color space set
by {{GPUExternalTextureDescriptor/colorSpace}}.
It is implementation-dependent whether, for any given external texture, the sampler (and filtering)
is applied before or after conversion from underlying values into the specified color space.

Note:
If the internal representation is an RGBA plane, sampling behaves as on a regular 2D texture.
If there are several underlying planes (e.g. Y+UV), the sampler is used to sample each
underlying texture separately, prior to conversion from YUV to the specified color space.


# Samplers # {#samplers}

<h3 id=gpusampler data-dfn-type=interface>`GPUSampler`
<span id=sampler-interface></span>
</h3>

A {{GPUSampler}} encodes transformations and filtering information that can
be used in a shader to interpret texture resource data.

{{GPUSampler}}s are created via {{GPUDevice/createSampler()}}.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUSampler {
};
GPUSampler includes GPUObjectBase;
</script>

{{GPUSampler}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUSampler data-timeline=const>
    : <dfn>\[[descriptor]]</dfn>, of type {{GPUSamplerDescriptor}}, readonly
    ::
        The {{GPUSamplerDescriptor}} with which the {{GPUSampler}} was created.

    : <dfn>\[[isComparison]]</dfn>, of type {{boolean}}, readonly
    ::
        Whether the {{GPUSampler}} is used as a comparison sampler.

    : <dfn>\[[isFiltering]]</dfn>, of type {{boolean}}, readonly
    ::
        Whether the {{GPUSampler}} weights multiple samples of a texture.
</dl>

### {{GPUSamplerDescriptor}} ### {#GPUSamplerDescriptor}

A {{GPUSamplerDescriptor}} specifies the options to use to create a {{GPUSampler}}.

<script type=idl>
dictionary GPUSamplerDescriptor
         : GPUObjectDescriptorBase {
    GPUAddressMode addressModeU = "clamp-to-edge";
    GPUAddressMode addressModeV = "clamp-to-edge";
    GPUAddressMode addressModeW = "clamp-to-edge";
    GPUFilterMode magFilter = "nearest";
    GPUFilterMode minFilter = "nearest";
    GPUMipmapFilterMode mipmapFilter = "nearest";
    float lodMinClamp = 0;
    float lodMaxClamp = 32;
    GPUCompareFunction compare;
    [Clamp] unsigned short maxAnisotropy = 1;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUSamplerDescriptor>
    : <dfn>addressModeU</dfn>
    : <dfn>addressModeV</dfn>
    : <dfn>addressModeW</dfn>
    ::
        Specifies the {{GPUAddressMode|address modes}} for the texture width, height, and depth
        coordinates, respectively.

    : <dfn>magFilter</dfn>
    ::
        Specifies the sampling behavior when the sampled area is smaller than or equal to one
        texel.

    : <dfn>minFilter</dfn>
    ::
        Specifies the sampling behavior when the sampled area is larger than one texel.

    : <dfn>mipmapFilter</dfn>
    ::
        Specifies behavior for sampling between mipmap levels.

    : <dfn>lodMinClamp</dfn>
    : <dfn>lodMaxClamp</dfn>
    ::
        Specifies the minimum and maximum [=levels of detail=], respectively, used internally when
        sampling a texture.

    : <dfn>compare</dfn>
    ::
        When provided the sampler will be a comparison sampler with the specified
        {{GPUCompareFunction}}.

        Note: Comparison samplers may use filtering, but the sampling results will be
        implementation-dependent and may differ from the normal filtering rules.

    : <dfn>maxAnisotropy</dfn>
    ::
        Specifies the maximum anisotropy value clamp used by the sampler. Anisotropic filtering is
        enabled when {{GPUSamplerDescriptor/maxAnisotropy}} is &gt; 1 and the implementation supports it.

        Anisotropic filtering improves the image quality of textures sampled at oblique viewing
        angles. Higher {{GPUSamplerDescriptor/maxAnisotropy}} values indicate the maximum ratio of
        anisotropy supported when filtering.

        <div class=note heading>
            Most implementations support {{GPUSamplerDescriptor/maxAnisotropy}} values in range
            between 1 and 16, inclusive. The used value of {{GPUSamplerDescriptor/maxAnisotropy}}
            will be clamped to the maximum value that the platform supports.

            The precise filtering behavior is implementation-dependent.
        </div>
</dl>

<dfn dfn lt="levels of detail|lod">Level of detail</dfn> (LOD) describes which mip level(s) are selected when sampling a
texture. It may be specified explicitly through shader methods like [=textureSampleLevel=] or implicitly determined from
the [=texture coordinate=] derivatives.

Note: See [[vulkan#textures-lod-and-scale-factor|Scale Factor Operation, LOD Operation and Image Level Selection]] in
the [[vulkan inline]] spec for an example of how implicit LODs may be calculated.

{{GPUAddressMode}} describes the behavior of the sampler if the sampled texels extend beyond the
bounds of the sampled texture.

<script type=idl>
enum GPUAddressMode {
    "clamp-to-edge",
    "repeat",
    "mirror-repeat",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUAddressMode>
    : <dfn>"clamp-to-edge"</dfn>
    ::
        Texture coordinates are clamped between 0.0 and 1.0, inclusive.

    : <dfn>"repeat"</dfn>
    ::
        Texture coordinates wrap to the other side of the texture.

    : <dfn>"mirror-repeat"</dfn>
    ::
        Texture coordinates wrap to the other side of the texture, but the texture is flipped
        when the integer part of the coordinate is odd.
</dl>

{{GPUFilterMode}} and {{GPUMipmapFilterMode}} describe the behavior of the sampler if the sampled
area does not cover exactly one texel.

Note: See [[vulkan#textures-texel-filtering|Texel Filtering]] in the [[vulkan inline]] spec for an example of how
samplers may determine which texels are sampled from for the various filtering modes.

<script type=idl>
enum GPUFilterMode {
    "nearest",
    "linear",
};

enum GPUMipmapFilterMode {
    "nearest",
    "linear",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUFilterMode>
    : <dfn>"nearest"</dfn>
    ::
        Return the value of the texel nearest to the texture coordinates.

    : <dfn>"linear"</dfn>
    ::
        Select two texels in each dimension and return a linear interpolation between their values.
</dl>

{{GPUCompareFunction}} specifies the behavior of a comparison sampler. If a comparison sampler is
used in a shader, the `depth_ref` is compared to the fetched texel value, and the result of this
comparison test is generated (`1.0f` for pass, or `0.0f` for fail).

After comparison, if texture filtering is enabled, the filtering step occurs, so that comparison
results are mixed together resulting in values in the range `[0, 1]`. Filtering **should** behave
as usual, however it **may** be computed with lower precision or not mix results at all.

<script type=idl>
enum GPUCompareFunction {
    "never",
    "less",
    "equal",
    "less-equal",
    "greater",
    "not-equal",
    "greater-equal",
    "always",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUCompareFunction>
    : <dfn>"never"</dfn>
    ::
        Comparison tests never pass.

    : <dfn>"less"</dfn>
    ::
        A provided value passes the comparison test if it is less than the sampled value.

    : <dfn>"equal"</dfn>
    ::
        A provided value passes the comparison test if it is equal to the sampled value.

    : <dfn>"less-equal"</dfn>
    ::
        A provided value passes the comparison test if it is less than or equal to the sampled value.

    : <dfn>"greater"</dfn>
    ::
        A provided value passes the comparison test if it is greater than the sampled value.

    : <dfn>"not-equal"</dfn>
    ::
        A provided value passes the comparison test if it is not equal to the sampled value.

    : <dfn>"greater-equal"</dfn>
    ::
        A provided value passes the comparison test if it is greater than or equal to the sampled value.

    : <dfn>"always"</dfn>
    ::
        Comparison tests always pass.
</dl>

### Sampler Creation ### {#sampler-creation}

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createSampler(descriptor)</dfn>
    ::
        Creates a {{GPUSampler}}.

        <div algorithm=GPUDevice.createSampler>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createSampler(descriptor)">
                    |descriptor|: Description of the {{GPUSampler}} to create.
                </pre>

                **Returns:** {{GPUSampler}}

                [=Content timeline=] steps:

                1. Let |s| be [=!=] [$create a new WebGPU object$](|this|, {{GPUSampler}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |s|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |s| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - |descriptor|.{{GPUSamplerDescriptor/lodMinClamp}} &ge; 0.
                        - |descriptor|.{{GPUSamplerDescriptor/lodMaxClamp}} &ge;
                            |descriptor|.{{GPUSamplerDescriptor/lodMinClamp}}.
                        - |descriptor|.{{GPUSamplerDescriptor/maxAnisotropy}} &ge; 1.

                            Note: Most implementations support {{GPUSamplerDescriptor/maxAnisotropy}}
                            values in range between 1 and 16, inclusive. The provided
                            {{GPUSamplerDescriptor/maxAnisotropy}} value will be clamped to the
                            maximum value that the platform supports.

                        - If |descriptor|.{{GPUSamplerDescriptor/maxAnisotropy}} &gt; 1:
                            - |descriptor|.{{GPUSamplerDescriptor/magFilter}},
                                |descriptor|.{{GPUSamplerDescriptor/minFilter}},
                                and |descriptor|.{{GPUSamplerDescriptor/mipmapFilter}} must be
                                {{GPUMipmapFilterMode/"linear"}}.
                    </div>
                1. Set |s|.{{GPUSampler/[[descriptor]]}} to |descriptor|.
                1. Set |s|.{{GPUSampler/[[isComparison]]}} to `false` if the {{GPUSamplerDescriptor/compare}} attribute
                        of |s|.{{GPUSampler/[[descriptor]]}} is `null` or undefined. Otherwise, set it to `true`.
                1. Set |s|.{{GPUSampler/[[isFiltering]]}} to `false` if none of {{GPUSamplerDescriptor/minFilter}},
                    {{GPUSamplerDescriptor/magFilter}}, or {{GPUSamplerDescriptor/mipmapFilter}} has the value of
                    {{GPUFilterMode/"linear"}}. Otherwise, set it to `true`.
            </div>
        </div>
</dl>

<div class=example>
    Creating a {{GPUSampler}} that does trilinear filtering and repeats texture coordinates:

    <pre highlight=js>
        const sampler = gpuDevice.createSampler({
            addressModeU: 'repeat',
            addressModeV: 'repeat',
            magFilter: 'linear',
            minFilter: 'linear',
            mipmapFilter: 'linear',
        });
    </pre>
</div>

# Resource Binding # {#bindings}

<h3 id=gpubindgrouplayout data-dfn-type=interface>`GPUBindGroupLayout`
<span id=bind-group-layout></span>
</h3>

A {{GPUBindGroupLayout}} defines the interface between a set of resources bound in a {{GPUBindGroup}} and their accessibility in shader stages.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUBindGroupLayout {
};
GPUBindGroupLayout includes GPUObjectBase;
</script>

{{GPUBindGroupLayout}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUBindGroupLayout>
    : <dfn>\[[descriptor]]</dfn>, of type {{GPUBindGroupLayoutDescriptor}}, readonly
    ::
</dl>

### Bind Group Layout Creation ### {#bind-group-layout-creation}

A {{GPUBindGroupLayout}} is created via {{GPUDevice/createBindGroupLayout()|GPUDevice.createBindGroupLayout()}}.

<script type=idl>
dictionary GPUBindGroupLayoutDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPUBindGroupLayoutEntry> entries;
};
</script>

{{GPUBindGroupLayoutDescriptor}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUBindGroupLayoutDescriptor>
    : <dfn>entries</dfn>
    ::
        A list of entries describing the shader resource bindings for a bind group.
</dl>

A {{GPUBindGroupLayoutEntry}} describes a single shader resource binding to be included in a {{GPUBindGroupLayout}}.

<script type=idl>
dictionary GPUBindGroupLayoutEntry {
    required GPUIndex32 binding;
    required GPUShaderStageFlags visibility;

    GPUBufferBindingLayout buffer;
    GPUSamplerBindingLayout sampler;
    GPUTextureBindingLayout texture;
    GPUStorageTextureBindingLayout storageTexture;
    GPUExternalTextureBindingLayout externalTexture;
};
</script>

{{GPUBindGroupLayoutEntry}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUBindGroupLayoutEntry>
    : <dfn>binding</dfn>
    ::
        A unique identifier for a resource binding within the {{GPUBindGroupLayout}}, corresponding
        to a {{GPUBindGroupEntry/binding|GPUBindGroupEntry.binding}} and a [=@binding=]
        attribute in the {{GPUShaderModule}}.

    : <dfn>visibility</dfn>
    ::
        A bitset of the members of {{GPUShaderStage}}.
        Each set bit indicates that a {{GPUBindGroupLayoutEntry}}'s resource
        will be accessible from the associated shader stage.

    : <dfn>buffer</dfn>
    : <dfn>sampler</dfn>
    : <dfn>texture</dfn>
    : <dfn>storageTexture</dfn>
    : <dfn>externalTexture</dfn>
    ::
        Exactly one of these members must be set, indicating the binding type.
        The contents of the member specify options specific to that type.

        The corresponding resource in {{GPUDevice/createBindGroup()}} requires
        the corresponding [=binding resource type=] for this binding.
</dl>

<script type=idl>
typedef [EnforceRange] unsigned long GPUShaderStageFlags;
[Exposed=(Window, Worker), SecureContext]
namespace GPUShaderStage {
    const GPUFlagsConstant VERTEX   = 0x1;
    const GPUFlagsConstant FRAGMENT = 0x2;
    const GPUFlagsConstant COMPUTE  = 0x4;
};
</script>

{{GPUShaderStage}} contains the following flags, which describe which shader stages a
corresponding {{GPUBindGroupEntry}} for this {{GPUBindGroupLayoutEntry}} will be visible to:

<dl dfn-type=const dfn-for=GPUShaderStage>
    : <dfn>VERTEX</dfn>
    ::
        The bind group entry will be accessible to vertex shaders.

    : <dfn>FRAGMENT</dfn>
    ::
        The bind group entry will be accessible to fragment shaders.

    : <dfn>COMPUTE</dfn>
    ::
        The bind group entry will be accessible to compute shaders.
</dl>

The [=binding member=] of a {{GPUBindGroupLayoutEntry}} is determined by which member of the
{{GPUBindGroupLayoutEntry}} is defined:
{{GPUBindGroupLayoutEntry/buffer}}, {{GPUBindGroupLayoutEntry/sampler}},
{{GPUBindGroupLayoutEntry/texture}}, {{GPUBindGroupLayoutEntry/storageTexture}}, or
{{GPUBindGroupLayoutEntry/externalTexture}}.
Only one may be defined for any given {{GPUBindGroupLayoutEntry}}.
Each member has an associated {{GPUBindingResource}}
type and each [=binding type=] has an associated [=internal usage=], given by this table:

<table class=data style="white-space: nowrap">
    <thead>
        <tr>
            <th><dfn dfn>Binding member</dfn>
            <th><dfn dfn lt="Binding Resource Type">Resource type</dfn>
            <th><dfn dfn>Binding type</dfn><br>
            <th><dfn dfn>Binding usage</dfn>
    </thead>
    <tr>
        <td rowspan=3>{{GPUBindGroupLayoutEntry/buffer}}
        <td rowspan=3>{{GPUBufferBinding}}<br>(or {{GPUBuffer}} as [$get as buffer binding|shorthand$])
        <td>{{GPUBufferBindingType/"uniform"}}
        <td>[=internal usage/constant=]
    <tr>
        <td>{{GPUBufferBindingType/"storage"}}
        <td>[=internal usage/storage=]
    <tr>
        <td>{{GPUBufferBindingType/"read-only-storage"}}
        <td>[=internal usage/storage-read=]

    <tr>
        <td rowspan=3>{{GPUBindGroupLayoutEntry/sampler}}
        <td rowspan=3>{{GPUSampler}}
        <td>{{GPUSamplerBindingType/"filtering"}}
        <td rowspan=3>[=internal usage/constant=]
    <tr>
        <td>{{GPUSamplerBindingType/"non-filtering"}}
    <tr>
        <td>{{GPUSamplerBindingType/"comparison"}}

    <tr>
        <td rowspan=5>{{GPUBindGroupLayoutEntry/texture}}
        <td rowspan=5>{{GPUTextureView}}<br>(or {{GPUTexture}} as [$get as texture view|shorthand$])
        <td>{{GPUTextureSampleType/"float"}}
        <td rowspan=5>[=internal usage/constant=]
    <tr>
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
    <tr>
        <td>{{GPUTextureSampleType/"depth"}}
    <tr>
        <td>{{GPUTextureSampleType/"sint"}}
    <tr>
        <td>{{GPUTextureSampleType/"uint"}}

    <tr>
        <td rowspan=3>{{GPUBindGroupLayoutEntry/storageTexture}}
        <td rowspan=3>{{GPUTextureView}}<br>(or {{GPUTexture}} as [$get as texture view|shorthand$])
        <td>{{GPUStorageTextureAccess/"write-only"}}
        <td rowspan=2>[=internal usage/storage=]
    <tr>
        <td>{{GPUStorageTextureAccess/"read-write"}}
    <tr>
        <td>{{GPUStorageTextureAccess/"read-only"}}
        <td>[=internal usage/storage-read=]

    <tr>
        <td>{{GPUBindGroupLayoutEntry/externalTexture}}
        <td>{{GPUExternalTexture}}<br>or {{GPUTextureView}}<br>(or {{GPUTexture}} as [$get as texture view|shorthand$])
        <td>
        <td>[=internal usage/constant=]
</table>

<div algorithm data-timeline=device>
    The [=list=] of {{GPUBindGroupLayoutEntry}} values |entries|
    <dfn>exceeds the binding slot limits</dfn> of [=supported limits=] |limits|
    if the number of slots used toward a limit exceeds the supported value in |limits|.
    Each entry may use multiple slots toward multiple limits.

    [=Device timeline=] steps:

    1. For each |entry| in |entries|, if:

        <dl class=switch>
            : |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
                is {{GPUBufferBindingType/"uniform"}} and
                |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`
            :: Consider 1 {{supported limits/maxDynamicUniformBuffersPerPipelineLayout}} slot to be used.
            : |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
                is {{GPUBufferBindingType/"storage"}} and
                |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`
            :: Consider 1 {{supported limits/maxDynamicStorageBuffersPerPipelineLayout}} slot to be used.
        </dl>
    1. For each shader stage |stage| in
        &laquo; {{GPUShaderStage/VERTEX}}, {{GPUShaderStage/FRAGMENT}}, {{GPUShaderStage/COMPUTE}} &raquo;:
        1. For each |entry| in |entries| for which
            |entry|.{{GPUBindGroupLayoutEntry/visibility}} contains |stage|, if:

            <dl class=switch>
                : |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
                    is {{GPUBufferBindingType/"uniform"}}
                :: Consider 1 {{supported limits/maxUniformBuffersPerShaderStage}} slot to be used.
                : |entry|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/type}}
                    is {{GPUBufferBindingType/"storage"}} or {{GPUBufferBindingType/"read-only-storage"}}
                :: Consider 1 {{supported limits/maxStorageBuffersPerShaderStage}} slot to be used.
                : |entry|.{{GPUBindGroupLayoutEntry/sampler}} is [=map/exist|provided=]
                :: Consider 1 {{supported limits/maxSamplersPerShaderStage}} slot to be used.
                : |entry|.{{GPUBindGroupLayoutEntry/texture}} is [=map/exist|provided=]
                :: Consider 1 {{supported limits/maxSampledTexturesPerShaderStage}} slot to be used.
                : |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} is [=map/exist|provided=]
                :: Consider 1 {{supported limits/maxStorageTexturesPerShaderStage}} slot to be used.
                : |entry|.{{GPUBindGroupLayoutEntry/externalTexture}} is [=map/exist|provided=]
                :: Consider
                    4 {{supported limits/maxSampledTexturesPerShaderStage}} slot,
                    1 {{supported limits/maxSamplersPerShaderStage}} slot, and
                    1 {{supported limits/maxUniformBuffersPerShaderStage}} slot
                    to be used.

                    Note: See {{GPUExternalTexture}} for an explanation of this behavior.
            </dl>
</div>

<script type=idl>
enum GPUBufferBindingType {
    "uniform",
    "storage",
    "read-only-storage",
};

dictionary GPUBufferBindingLayout {
    GPUBufferBindingType type = "uniform";
    boolean hasDynamicOffset = false;
    GPUSize64 minBindingSize = 0;
};
</script>

{{GPUBufferBindingLayout}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUBufferBindingLayout>
    : <dfn>type</dfn>
    ::
        Indicates the type required for buffers bound to this bindings.

    : <dfn>hasDynamicOffset</dfn>
    ::
        Indicates whether this binding requires a dynamic offset.

    : <dfn>minBindingSize</dfn>
    ::
        Indicates the minimum {{GPUBufferBinding/size}} of a buffer binding used with this bind point.

        Bindings are always validated against this size in {{GPUDevice/createBindGroup()}}.

        If this *is not* `0`, pipeline creation additionally [$validating shader binding|validates$]
        that this value &ge; the [=minimum buffer binding size=] of the variable.

        If this *is* `0`, it is ignored by pipeline creation, and instead draw/dispatch commands
        [$Validate encoder bind groups|validate$] that each binding in the {{GPUBindGroup}}
        satisfies the [=minimum buffer binding size=] of the variable.

        Note:
        Similar execution-time validation is theoretically possible for other
        binding-related fields specified for early validation, like
        {{GPUTextureBindingLayout/sampleType}} and {{GPUStorageTextureBindingLayout/format}},
        which currently can only be validated in pipeline creation.
        However, such execution-time validation could be costly or unnecessarily complex, so it is
        available only for {{GPUBufferBindingLayout/minBindingSize}} which is expected to have the
        most ergonomic impact.
</dl>

<script type=idl>
enum GPUSamplerBindingType {
    "filtering",
    "non-filtering",
    "comparison",
};

dictionary GPUSamplerBindingLayout {
    GPUSamplerBindingType type = "filtering";
};
</script>

{{GPUSamplerBindingLayout}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUSamplerBindingLayout>
    : <dfn>type</dfn>
    ::
        Indicates the required type of a sampler bound to this bindings.
</dl>

<script type=idl>
enum GPUTextureSampleType {
    "float",
    "unfilterable-float",
    "depth",
    "sint",
    "uint",
};

dictionary GPUTextureBindingLayout {
    GPUTextureSampleType sampleType = "float";
    GPUTextureViewDimension viewDimension = "2d";
    boolean multisampled = false;
};
</script>

{{GPUTextureBindingLayout}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUTextureBindingLayout>
    : <dfn>sampleType</dfn>
    ::
        Indicates the type required for texture views bound to this binding.

    : <dfn>viewDimension</dfn>
    ::
        Indicates the required {{GPUTextureViewDescriptor/dimension}} for texture views bound to
        this binding.

    : <dfn>multisampled</dfn>
    ::
        Indicates whether or not texture views bound to this binding must be multisampled.
</dl>

<script type=idl>
enum GPUStorageTextureAccess {
    "write-only",
    "read-only",
    "read-write",
};

dictionary GPUStorageTextureBindingLayout {
    GPUStorageTextureAccess access = "write-only";
    required GPUTextureFormat format;
    GPUTextureViewDimension viewDimension = "2d";
};
</script>

{{GPUStorageTextureBindingLayout}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUStorageTextureBindingLayout>
    : <dfn>access</dfn>
    ::
        The access mode for this binding, indicating readability and writability.

    : <dfn>format</dfn>
    ::
        The required {{GPUTextureViewDescriptor/format}} of texture views bound to this binding.

    : <dfn>viewDimension</dfn>
    ::
        Indicates the required {{GPUTextureViewDescriptor/dimension}} for texture views bound to
        this binding.
</dl>

<script type=idl>
dictionary GPUExternalTextureBindingLayout {
};
</script>

A {{GPUBindGroupLayout}} object has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBindGroupLayout data-timeline=device>
    : <dfn>\[[entryMap]]</dfn>, of type [=ordered map=]&lt;{{GPUSize32}}, {{GPUBindGroupLayoutEntry}}&gt;, readonly
    ::
        The map of binding indices pointing to the {{GPUBindGroupLayoutEntry}}s,
        which this {{GPUBindGroupLayout}} describes.

    : <dfn>\[[dynamicOffsetCount]]</dfn>, of type {{GPUSize32}}, readonly
    ::
        The number of buffer bindings with dynamic offsets in this {{GPUBindGroupLayout}}.

    : <dfn>\[[exclusivePipeline]]</dfn>, of type {{GPUPipelineBase}}?, readonly
    ::
        The pipeline that created this {{GPUBindGroupLayout}}, if it was created as part of a
        [[#default-pipeline-layout|default pipeline layout]]. If not `null`, {{GPUBindGroup}}s
        created with this {{GPUBindGroupLayout}} can only be used with the specified
        {{GPUPipelineBase}}.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createBindGroupLayout(descriptor)</dfn>
    ::
        Creates a {{GPUBindGroupLayout}}.

        <div algorithm=GPUDevice.createBindGroupLayout>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createBindGroupLayout(descriptor)">
                    |descriptor|: Description of the {{GPUBindGroupLayout}} to create.
                </pre>

                **Returns:** {{GPUBindGroupLayout}}

                [=Content timeline=] steps:

                1. For each {{GPUBindGroupLayoutEntry}} |entry| in |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}}:
                    1. If |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} is [=map/exist|provided=]:
                        1. [=?=] [$Validate texture format required features$] for
                            |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/format}}
                            with |this|.{{GPUObjectBase/[[device]]}}.
                1. Let |layout| be [=!=] [$create a new WebGPU object$](|this|, {{GPUBindGroupLayout}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |layout|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |layout| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
                        - The {{GPUBindGroupLayoutEntry/binding}} of each entry in |descriptor| is unique.
                        - The {{GPUBindGroupLayoutEntry/binding}} of each entry in |descriptor| must be &lt;
                            |limits|.{{supported limits/maxBindingsPerBindGroup}}.
                        - |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}} must not
                            [=exceeds the binding slot limits|exceed the binding slot limits=] of |limits|.
                        - For each {{GPUBindGroupLayoutEntry}} |entry| in |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}}:
                            - Exactly one of
                                |entry|.{{GPUBindGroupLayoutEntry/buffer}},
                                |entry|.{{GPUBindGroupLayoutEntry/sampler}},
                                |entry|.{{GPUBindGroupLayoutEntry/texture}},
                                |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}, and
                                |entry|.{{GPUBindGroupLayoutEntry/externalTexture}} is [=map/exist|provided=].

                            - |entry|.{{GPUBindGroupLayoutEntry/visibility}} contains only bits defined in {{GPUShaderStage}}.

                            - If |entry|.{{GPUBindGroupLayoutEntry/visibility}} includes
                                {{GPUShaderStage/VERTEX}}:
                                - If |entry|.{{GPUBindGroupLayoutEntry/buffer}} is [=map/exists|provided=],
                                    |entry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}}
                                    must be {{GPUBufferBindingType/"uniform"}} or {{GPUBufferBindingType/"read-only-storage"}}.
                                - If |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} is [=map/exists|provided=],
                                    |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/access}}
                                    must be {{GPUStorageTextureAccess/"read-only"}}.

                            - If |entry|.{{GPUBindGroupLayoutEntry/texture}}?.{{GPUTextureBindingLayout/multisampled}} is `true`:
                                - |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/viewDimension}} is
                                    {{GPUTextureViewDimension/"2d"}}.
                                - |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}} is not
                                    {{GPUTextureSampleType/"float"}}.

                            - If |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} is [=map/exist|provided=]:
                                - |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/viewDimension}} is not
                                    {{GPUTextureViewDimension/"cube"}} or {{GPUTextureViewDimension/"cube-array"}}.
                                - |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/format}} must be a format
                                    which can support storage usage for the given
                                    |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/access}}
                                    according to the [[#plain-color-formats]] table.

                    </div>

                1. Set |layout|.{{GPUBindGroupLayout/[[descriptor]]}} to |descriptor|.
                1. Set |layout|.{{GPUBindGroupLayout/[[dynamicOffsetCount]]}} to the number of
                    entries in |descriptor| where {{GPUBindGroupLayoutEntry/buffer}} is [=map/exist|provided=] and
                    {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`.
                1. Set |layout|.{{GPUBindGroupLayout/[[exclusivePipeline]]}} to `null`.
                1. For each {{GPUBindGroupLayoutEntry}} |entry| in
                    |descriptor|.{{GPUBindGroupLayoutDescriptor/entries}}:
                    1. Insert |entry| into |layout|.{{GPUBindGroupLayout/[[entryMap]]}}
                        with the key of |entry|.{{GPUBindGroupLayoutEntry/binding}}.
            </div>
        </div>
</dl>

### Compatibility ### {#bind-group-compatibility}

<div algorithm data-timeline=device>
Two {{GPUBindGroupLayout}} objects |a| and |b| are considered <dfn dfn>group-equivalent</dfn>
if and only if all of the following conditions are satisfied:

    <div class=validusage>
        - |a|.{{GPUBindGroupLayout/[[exclusivePipeline]]}} == |b|.{{GPUBindGroupLayout/[[exclusivePipeline]]}}.
        - for any binding number |binding|, one of the following conditions is satisfied:
            - it's missing from both |a|.{{GPUBindGroupLayout/[[entryMap]]}} and |b|.{{GPUBindGroupLayout/[[entryMap]]}}.
            - |a|.{{GPUBindGroupLayout/[[entryMap]]}}[|binding|] == |b|.{{GPUBindGroupLayout/[[entryMap]]}}[|binding|]
    </div>
</div>

If bind groups layouts are [=group-equivalent=] they can be interchangeably used in all contents.

<h3 id=gpubindgroup data-dfn-type=interface>`GPUBindGroup`
<span id=gpu-bind-group></span>
</h3>

A {{GPUBindGroup}} defines a set of resources to be bound together in a group
and how the resources are used in shader stages.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUBindGroup {
};
GPUBindGroup includes GPUObjectBase;
</script>

{{GPUBindGroup}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBindGroup data-timeline=device>
    : <dfn>\[[layout]]</dfn>, of type {{GPUBindGroupLayout}}, readonly
    ::
        The {{GPUBindGroupLayout}} associated with this {{GPUBindGroup}}.

    : <dfn>\[[entries]]</dfn>, of type [=sequence=]&lt;{{GPUBindGroupEntry}}&gt;, readonly
    ::
        The set of {{GPUBindGroupEntry}}s this {{GPUBindGroup}} describes.

    : <dfn>\[[usedResources]]</dfn>, of type [=usage scope=], readonly
    ::
        The set of buffer and texture [=subresource=]s used by this bind group,
        associated with lists of the [=internal usage=] flags.
</dl>

<div algorithm data-timeline=device>
    The <dfn for=GPUBindGroup>bound buffer ranges</dfn> of a {{GPUBindGroup}} |bindGroup|,
    given [=list=]&lt;GPUBufferDynamicOffset&gt; |dynamicOffsets|, are computed as follows:

    1. Let |result| be a new [=set=]&lt;({{GPUBindGroupLayoutEntry}}, {{GPUBufferBinding}})&gt;.
    1. Let |dynamicOffsetIndex| be 0.
    1. For each {{GPUBindGroupEntry}} |bindGroupEntry| in |bindGroup|.{{GPUBindGroup/[[entries]]}},
        sorted by |bindGroupEntry|.{{GPUBindGroupEntry/binding}}:
        1. Let |bindGroupLayoutEntry| be
            |bindGroup|.{{GPUBindGroup/[[layout]]}}.{{GPUBindGroupLayout/[[entryMap]]}}[|bindGroupEntry|.{{GPUBindGroupEntry/binding}}].
        1. If |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/buffer}} is not
            [=map/exists|provided=], **continue**.
        1. Let |bound| be [$get as buffer binding$](|bindGroupEntry|.{{GPUBindGroupEntry/resource}}).
        1. If |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}}:
            1. Increment |bound|.{{GPUBufferBinding/offset}} by
                |dynamicOffsets|[|dynamicOffsetIndex|].
            1. Increment |dynamicOffsetIndex| by 1.
        1. [=set/Append=] (|bindGroupLayoutEntry|, |bound|) to |result|.
    1. Return |result|.
</div>

### Bind Group Creation ### {#bind-group-creation}

A {{GPUBindGroup}} is created via {{GPUDevice/createBindGroup()|GPUDevice.createBindGroup()}}.

<script type=idl>
dictionary GPUBindGroupDescriptor
         : GPUObjectDescriptorBase {
    required GPUBindGroupLayout layout;
    required sequence<GPUBindGroupEntry> entries;
};
</script>

{{GPUBindGroupDescriptor}} dictionaries have the following members:

<dl dfn-type=dict-member dfn-for=GPUBindGroupDescriptor>
    : <dfn>layout</dfn>
    ::
        The {{GPUBindGroupLayout}} the entries of this bind group will conform to.

    : <dfn>entries</dfn>
    ::
        A list of entries describing the resources to expose to the shader for each binding
        described by the {{GPUBindGroupDescriptor/layout}}.
</dl>

<script type=idl>
typedef (GPUSampler or
         GPUTexture or
         GPUTextureView or
         GPUBuffer or
         GPUBufferBinding or
         GPUExternalTexture) GPUBindingResource;

dictionary GPUBindGroupEntry {
    required GPUIndex32 binding;
    required GPUBindingResource resource;
};
</script>

A {{GPUBindGroupEntry}} describes a single resource to be bound in a {{GPUBindGroup}}, and has the
following members:

<dl dfn-type=dict-member dfn-for=GPUBindGroupEntry>
    : <dfn>binding</dfn>
    ::
        A unique identifier for a resource binding within the {{GPUBindGroup}}, corresponding to a
        {{GPUBindGroupLayoutEntry/binding|GPUBindGroupLayoutEntry.binding}} and a [=@binding=]
        attribute in the {{GPUShaderModule}}.

    : <dfn>resource</dfn>
    ::
        The resource to bind, which may be a {{GPUSampler}}, {{GPUTexture}}, {{GPUTextureView}},
        {{GPUBuffer}}, {{GPUBufferBinding}}, or {{GPUExternalTexture}}.
</dl>

{{GPUBindGroupEntry}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBindGroupEntry data-timeline=device>
    : <dfn>\[[prevalidatedSize]]</dfn>, of type {{boolean}}
    ::
        Whether or not this binding entry had its buffer size validated at time of creation.
</dl>

<script type=idl>
dictionary GPUBufferBinding {
    required GPUBuffer buffer;
    GPUSize64 offset = 0;
    GPUSize64 size;
};
</script>

A {{GPUBufferBinding}} describes a buffer and optional range to bind as a resource, and has the
following members:

<dl dfn-type=dict-member dfn-for=GPUBufferBinding>
    : <dfn>buffer</dfn>
    ::
        The {{GPUBuffer}} to bind.

    : <dfn>offset</dfn>
    ::
        The offset, in bytes, from the beginning of {{GPUBufferBinding/buffer}} to the
        beginning of the range exposed to the shader by the buffer binding.

    : <dfn>size</dfn>
    ::
        The size, in bytes, of the buffer binding.
        If not [=map/exist|provided=], specifies the range starting at
        {{GPUBufferBinding/offset}} and ending at the end of {{GPUBufferBinding/buffer}}.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createBindGroup(descriptor)</dfn>
    ::
        Creates a {{GPUBindGroup}}.

        <div algorithm=GPUDevice.createBindGroup>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createBindGroup(descriptor)">
                    |descriptor|: Description of the {{GPUBindGroup}} to create.
                </pre>

                **Returns:** {{GPUBindGroup}}

                [=Content timeline=] steps:

                1. Let |bindGroup| be [=!=] [$create a new WebGPU object$](|this|, {{GPUBindGroup}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |bindGroup|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |bindGroup| and return.

                    <div class=validusage>
                        - |descriptor|.{{GPUBindGroupDescriptor/layout}} is [$valid to use with$] |this|.
                        - The number of {{GPUBindGroupLayoutDescriptor/entries}} of
                            |descriptor|.{{GPUBindGroupDescriptor/layout}} is exactly equal to
                            the number of |descriptor|.{{GPUBindGroupDescriptor/entries}}.

                        For each {{GPUBindGroupEntry}} |bindingDescriptor| in
                            |descriptor|.{{GPUBindGroupDescriptor/entries}}:
                            - Let |resource| be |bindingDescriptor|.{{GPUBindGroupEntry/resource}}.
                            - There is exactly one {{GPUBindGroupLayoutEntry}} |layoutBinding|
                                in |descriptor|.{{GPUBindGroupDescriptor/layout}}.{{GPUBindGroupLayoutDescriptor/entries}}
                                such that |layoutBinding|.{{GPUBindGroupLayoutEntry/binding}} equals to
                                |bindingDescriptor|.{{GPUBindGroupEntry/binding}}.

                            - If the defined [=binding member=] for |layoutBinding| is:

                                <dl class=switch>
                                    : {{GPUBindGroupLayoutEntry/sampler}}
                                    ::
                                        - |resource| is a {{GPUSampler}}.
                                        - |resource| is [$valid to use with$] |this|.
                                        - If |layoutBinding|.{{GPUBindGroupLayoutEntry/sampler}}.{{GPUSamplerBindingLayout/type}} is:

                                            <dl class=switch>
                                                : {{GPUSamplerBindingType/"filtering"}}
                                                :: |resource|.{{GPUSampler/[[isComparison]]}} is `false`.

                                                : {{GPUSamplerBindingType/"non-filtering"}}
                                                ::
                                                    |resource|.{{GPUSampler/[[isFiltering]]}} is `false`.
                                                    |resource|.{{GPUSampler/[[isComparison]]}} is `false`.

                                                : {{GPUSamplerBindingType/"comparison"}}
                                                :: |resource|.{{GPUSampler/[[isComparison]]}} is `true`.
                                            </dl>

                                    : {{GPUBindGroupLayoutEntry/texture}}
                                    ::
                                        - |resource| is either a {{GPUTexture}} or a {{GPUTextureView}}.
                                        - |resource| is [$valid to use with$] |this|.
                                        - Let |textureView| be [$get as texture view$](|resource|).
                                        - Let |texture| be |textureView|.{{GPUTextureView/[[texture]]}}.
                                        - |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/viewDimension}}
                                            is equal to |textureView|'s {{GPUTextureViewDescriptor/dimension}}.
                                        - |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}}
                                            is [[#texture-format-caps|compatible]] with
                                            |textureView|'s {{GPUTextureViewDescriptor/format}}.
                                        - |textureView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/usage}}
                                            includes {{GPUTextureUsage/TEXTURE_BINDING}}.
                                        - If |layoutBinding|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/multisampled}}
                                            is `true`, |texture|'s {{GPUTextureDescriptor/sampleCount}}
                                            &gt; `1`, Otherwise |texture|'s {{GPUTextureDescriptor/sampleCount}} is `1`.

                                    : {{GPUBindGroupLayoutEntry/storageTexture}}
                                    ::
                                        - |resource| is either a {{GPUTexture}} or a {{GPUTextureView}}.
                                        - |resource| is [$valid to use with$] |this|.
                                        - Let |storageTextureView| be [$get as texture view$](|resource|).
                                        - Let |texture| be |storageTextureView|.{{GPUTextureView/[[texture]]}}.
                                        - |layoutBinding|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/viewDimension}}
                                            is equal to |storageTextureView|'s {{GPUTextureViewDescriptor/dimension}}.
                                        - |layoutBinding|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/format}}
                                            is equal to |storageTextureView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}}.
                                        - |storageTextureView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/usage}}
                                            includes {{GPUTextureUsage/STORAGE_BINDING}}.
                                        - |storageTextureView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/mipLevelCount}} must be 1.

                                    :  {{GPUBindGroupLayoutEntry/buffer}}
                                    ::
                                        - |resource| is either a {{GPUBuffer}} or a {{GPUBufferBinding}}.
                                        - Let |bufferBinding| be [$get as buffer binding$](|resource|).
                                        - |bufferBinding|.{{GPUBufferBinding/buffer}} is [$valid to use with$] |this|.
                                        - The bound part designated by |bufferBinding|.{{GPUBufferBinding/offset}} and
                                            |bufferBinding|.{{GPUBufferBinding/size}} resides inside the buffer and has non-zero size.
                                        - [$effective buffer binding size$](|bufferBinding|) &ge;
                                            |layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}.

                                        - If |layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} is

                                            <dl class=switch>
                                                : {{GPUBufferBindingType/"uniform"}}
                                                ::
                                                    - |bufferBinding|.{{GPUBufferBinding/buffer}}.{{GPUBufferDescriptor/usage}}
                                                        includes {{GPUBufferUsage/UNIFORM}}.
                                                    - [$effective buffer binding size$](|bufferBinding|) &le;
                                                        |limits|.{{supported limits/maxUniformBufferBindingSize}}.
                                                    - |bufferBinding|.{{GPUBufferBinding/offset}} is a multiple of
                                                        |limits|.{{supported limits/minUniformBufferOffsetAlignment}}.

                                                : {{GPUBufferBindingType/"storage"}} or
                                                    {{GPUBufferBindingType/"read-only-storage"}}
                                                ::
                                                    - |bufferBinding|.{{GPUBufferBinding/buffer}}.{{GPUBufferDescriptor/usage}}
                                                        includes {{GPUBufferUsage/STORAGE}}.
                                                    - [$effective buffer binding size$](|bufferBinding|) &le;
                                                        |limits|.{{supported limits/maxStorageBufferBindingSize}}.
                                                    - [$effective buffer binding size$](|bufferBinding|) is a multiple of 4.
                                                    - |bufferBinding|.{{GPUBufferBinding/offset}} is a multiple of
                                                        |limits|.{{supported limits/minStorageBufferOffsetAlignment}}.
                                            </dl>

                                    :  {{GPUBindGroupLayoutEntry/externalTexture}}
                                    ::
                                        - |resource| is either a {{GPUExternalTexture}}, a {{GPUTexture}}, or a {{GPUTextureView}}.
                                        - |resource| is [$valid to use with$] |this|.
                                        - If |resource| is a:

                                            <dl class=switch>
                                                : {{GPUTexture}} or {{GPUTextureView}}
                                                ::
                                                    - Let |view| be [$get as texture view$](|resource|).
                                                    - |view|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/usage}}
                                                        must include {{GPUTextureUsage/TEXTURE_BINDING}}.
                                                    - |view|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/dimension}}
                                                        must be {{GPUTextureViewDimension/"2d"}}.
                                                    - |view|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/mipLevelCount}}
                                                        must be 1.
                                                    - |view|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}}
                                                        must be {{GPUTextureFormat/"rgba8unorm"}},
                                                        {{GPUTextureFormat/"bgra8unorm"}}, or
                                                        {{GPUTextureFormat/"rgba16float"}}.
                                                    - |view|.{{GPUTextureView/[[texture]]}}.{{GPUTexture/sampleCount}}
                                                        must be 1.
                                            </dl>
                                </dl>
                    </div>

                1. Let |bindGroup|.{{GPUBindGroup/[[layout]]}} =
                    |descriptor|.{{GPUBindGroupDescriptor/layout}}.
                1. Let |bindGroup|.{{GPUBindGroup/[[entries]]}} =
                    |descriptor|.{{GPUBindGroupDescriptor/entries}}.
                1. Let |bindGroup|.{{GPUBindGroup/[[usedResources]]}} = {}.

                1. For each {{GPUBindGroupEntry}} |bindingDescriptor| in
                    |descriptor|.{{GPUBindGroupDescriptor/entries}}:
                    1. Let |internalUsage| be the [=binding usage=] for |layoutBinding|.
                    1. Each [=subresource=] seen by |resource| is added to {{GPUBindGroup/[[usedResources]]}} as |internalUsage|.
                    1. Let |bindingDescriptor|.{{GPUBindGroupEntry/[[prevalidatedSize]]}} be `false` if the defined
                        [=binding member=] for |layoutBinding| is {{GPUBindGroupLayoutEntry/buffer}}
                        and |layoutBinding|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}
                        is `0`, and `true` otherwise.
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>get as texture view</dfn>(|resource|)

    **Arguments:**

    - {{GPUBindingResource}} |resource|

    **Returns:** {{GPUTextureView}}

    1. [=Assert=] |resource| is either a {{GPUTexture}} or a {{GPUTextureView}}.
    1. If |resource| is a:
        <dl class=switch>
            : {{GPUTexture}}
            ::
                1. Return |resource|.{{GPUTexture/createView()}}.
            : {{GPUTextureView}}
            ::
                1. Return |resource|.
        </dl>
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>get as buffer binding</dfn>(|resource|)

    **Arguments:**

    - {{GPUBindingResource}} |resource|

    **Returns:** {{GPUBufferBinding}}

    1. [=Assert=] |resource| is either a {{GPUBuffer}} or a {{GPUBufferBinding}}.
    1. If |resource| is a:
        <dl class=switch>
            : {{GPUBuffer}}
            ::
                1. Let |bufferBinding| a new {{GPUBufferBinding}}.
                1. Set |bufferBinding|.{{GPUBufferBinding/buffer}} to |resource|.
                1. Return |bufferBinding|.
            : {{GPUBufferBinding}}
            ::
                1. Return |resource|.
        </dl>
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>effective buffer binding size</dfn>(|binding|)

    **Arguments:**

    - {{GPUBufferBinding}} |binding|

    **Returns:** {{GPUSize64}}

    1. If |binding|.{{GPUBufferBinding/size}} is not [=map/exist|provided=]:
        1. Return max(0, |binding|.{{GPUBufferBinding/buffer}}.{{GPUBuffer/size}} - |binding|.{{GPUBufferBinding/offset}});
    1. Return |binding|.{{GPUBufferBinding/size}}.
</div>

<div algorithm data-timeline=device>
    Two {{GPUBufferBinding}} objects |a| and |b| are considered <dfn dfn>buffer-binding-aliasing</dfn> if and only if all of the following are true:

    - |a|.{{GPUBufferBinding/buffer}} == |b|.{{GPUBufferBinding/buffer}}
    - The range formed by |a|.{{GPUBufferBinding/offset}} and |a|.{{GPUBufferBinding/size}} intersects
        the range formed by |b|.{{GPUBufferBinding/offset}} and |b|.{{GPUBufferBinding/size}},
        where if a {{GPUBufferBinding/size}} is [=map/exists|unspecified=],
        the range goes to the end of the buffer.

    Note: When doing this calculation, any dynamic offsets have already been applied to the ranges.
</div>

<h3 id=gpupipelinelayout data-dfn-type=interface>`GPUPipelineLayout`
<span id=pipeline-layout></span>
</h3>

A {{GPUPipelineLayout}} defines the mapping between resources of all {{GPUBindGroup}} objects set up during command encoding in [=GPUBindingCommandsMixin/setBindGroup()=], and the shaders of the pipeline set by {{GPURenderCommandsMixin/setPipeline(pipeline)|GPURenderCommandsMixin.setPipeline}} or {{GPUComputePassEncoder/setPipeline(pipeline)|GPUComputePassEncoder.setPipeline}}.

The full binding address of a resource can be defined as a trio of:

1. shader stage mask, to which the resource is visible
1. bind group index
1. binding number

The components of this address can also be seen as the binding space of a pipeline. A {{GPUBindGroup}} (with the corresponding {{GPUBindGroupLayout}}) covers that space for a fixed bind group index. The contained bindings need to be a superset of the resources used by the shader at this bind group index.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUPipelineLayout {
};
GPUPipelineLayout includes GPUObjectBase;
</script>

{{GPUPipelineLayout}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUPipelineLayout data-timeline=device>
    : <dfn>\[[bindGroupLayouts]]</dfn>, of type [=list=]&lt;{{GPUBindGroupLayout}}&gt;, readonly
    ::
        The {{GPUBindGroupLayout}} objects provided at creation in {{GPUPipelineLayoutDescriptor/bindGroupLayouts|GPUPipelineLayoutDescriptor.bindGroupLayouts}}.
</dl>

Note: using the same {{GPUPipelineLayout}} for many {{GPURenderPipeline}} or {{GPUComputePipeline}} pipelines guarantees that the user agent doesn't need to rebind any resources internally when there is a switch between these pipelines.

<div class=example>
    {{GPUComputePipeline}} object X was created with {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGroupLayouts}} A, B, C. {{GPUComputePipeline}} object Y was created with {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGroupLayouts}} A, D, C. Supposing the command encoding sequence has two dispatches:

    1. [=GPUBindingCommandsMixin/setBindGroup()|setBindGroup=](0, ...)
    1. [=GPUBindingCommandsMixin/setBindGroup()|setBindGroup=](1, ...)
    1. [=GPUBindingCommandsMixin/setBindGroup()|setBindGroup=](2, ...)
    1. {{GPUComputePassEncoder/setPipeline()|setPipeline}}(X)
    1. {{GPUComputePassEncoder/dispatchWorkgroups()|dispatchWorkgroups}}()
    1. [=GPUBindingCommandsMixin/setBindGroup()|setBindGroup=](1, ...)
    1. {{GPUComputePassEncoder/setPipeline()|setPipeline}}(Y)
    1. {{GPUComputePassEncoder/dispatchWorkgroups()|dispatchWorkgroups}}()

    In this scenario, the user agent would have to re-bind the group slot 2 for the second dispatch, even though neither the {{GPUBindGroupLayout}} at index 2 of {{GPUPipelineLayout/[[bindGroupLayouts]]|GPUPipelineLayout.bindGroupLayouts}}, or the {{GPUBindGroup}} at slot 2, change.
</div>

Note: the expected usage of the {{GPUPipelineLayout}} is placing the most common and the least frequently changing bind groups at the "bottom" of the layout, meaning lower bind group slot numbers, like 0 or 1. The more frequently a bind group needs to change between draw calls, the higher its index should be. This general guideline allows the user agent to minimize state changes between draw calls, and consequently lower the CPU overhead.

### Pipeline Layout Creation ### {#pipeline-layout-creation}

A {{GPUPipelineLayout}} is created via {{GPUDevice/createPipelineLayout()|GPUDevice.createPipelineLayout()}}.

<script type=idl>
dictionary GPUPipelineLayoutDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPUBindGroupLayout?> bindGroupLayouts;
};
</script>

{{GPUPipelineLayoutDescriptor}} dictionaries define all the {{GPUBindGroupLayout}}s used by a
pipeline, and have the following members:

<dl dfn-type=dict-member dfn-for=GPUPipelineLayoutDescriptor>
    : <dfn>bindGroupLayouts</dfn>
    ::
        A list of optional {{GPUBindGroupLayout}}s the pipeline will use. Each element corresponds
        to a [=@group=] attribute in the {{GPUShaderModule}}, with the `N`th element corresponding
        with `@group(N)`.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createPipelineLayout(descriptor)</dfn>
    ::
        Creates a {{GPUPipelineLayout}}.

        <div algorithm=GPUDevice.createPipelineLayout>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createPipelineLayout(descriptor)">
                    |descriptor|: Description of the {{GPUPipelineLayout}} to create.
                </pre>

                **Returns:** {{GPUPipelineLayout}}

                [=Content timeline=] steps:

                1. Let |pl| be [=!=] [$create a new WebGPU object$](|this|, {{GPUPipelineLayout}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |pl|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
                1. Let |bindGroupLayouts| be a [=list=] of `null` {{GPUBindGroupLayout}}s with [=list/size=]
                     equal to |limits|.{{supported limits/maxBindGroups}}.
                1. [=list/For each=] |bindGroupLayout| at index |i| in |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}:
                    1. If |bindGroupLayout| is not `null` and
                        |bindGroupLayout|.{{GPUBindGroupLayout/[[descriptor]]}}.{{GPUBindGroupLayoutDescriptor/entries}}
                        is not [=list/empty=]:
                        1. Set |bindGroupLayouts|[|i|] to |bindGroupLayout|.
                1. Let |allEntries| be the result of concatenating
                    |bgl|.{{GPUBindGroupLayout/[[descriptor]]}}.{{GPUBindGroupLayoutDescriptor/entries}}
                    for all non-`null` |bgl| in |bindGroupLayouts|.
                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |pl| and return.

                    <div class=validusage>
                        - Every non-`null` {{GPUBindGroupLayout}} in |bindGroupLayouts|
                            must be [$valid to use with$] |this| and have a {{GPUBindGroupLayout/[[exclusivePipeline]]}}
                            of `null`.
                        - The [=list/size=] of |descriptor|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}}
                            must be &le; |limits|.{{supported limits/maxBindGroups}}.
                        - |allEntries| must not [=exceeds the binding slot limits|exceed the binding slot limits=] of |limits|.
                    </div>

                1. Set the |pl|.{{GPUPipelineLayout/[[bindGroupLayouts]]}} to |bindGroupLayouts|.
            </div>
        </div>
</dl>

Note: two {{GPUPipelineLayout}} objects are considered equivalent for any usage
if their internal {{GPUPipelineLayout/[[bindGroupLayouts]]}} sequences contain
{{GPUBindGroupLayout}} objects that are [=group-equivalent=].

## Example ## {#bindgroup-examples}

<div class=example>
    Create a {{GPUBindGroupLayout}} that describes a binding with a uniform buffer, a texture, and a sampler.
    Then create a {{GPUBindGroup}} and a {{GPUPipelineLayout}} using the {{GPUBindGroupLayout}}.

    <pre highlight=js>
        const bindGroupLayout = gpuDevice.createBindGroupLayout({
            entries: [{
                binding: 0,
                visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT,
                buffer: {}
            }, {
                binding: 1,
                visibility: GPUShaderStage.FRAGMENT,
                texture: {}
            }, {
                binding: 2,
                visibility: GPUShaderStage.FRAGMENT,
                sampler: {}
            }]
        });

        const bindGroup = gpuDevice.createBindGroup({
            layout: bindGroupLayout,
            entries: [{
                binding: 0,
                resource: { buffer: buffer },
            }, {
                binding: 1,
                resource: texture
            }, {
                binding: 2,
                resource: sampler
            }]
        });

        const pipelineLayout = gpuDevice.createPipelineLayout({
            bindGroupLayouts: [bindGroupLayout]
        });
    </pre>
</div>

# Shader Modules # {#shader-modules}

<h3 id=gpushadermodule data-dfn-type=interface>`GPUShaderModule`
<span id=shader-module></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUShaderModule {
    Promise<GPUCompilationInfo> getCompilationInfo();
};
GPUShaderModule includes GPUObjectBase;
</script>

{{GPUShaderModule}} is a reference to an internal shader module object.

### Shader Module Creation ### {#shader-module-creation}

<script type=idl>
dictionary GPUShaderModuleDescriptor
         : GPUObjectDescriptorBase {
    required USVString code;
    sequence<GPUShaderModuleCompilationHint> compilationHints = [];
};
</script>

<dl dfn-type=dict-member dfn-for=GPUShaderModuleDescriptor>
    : <dfn>code</dfn>
    ::
        The <a href="https://gpuweb.github.io/gpuweb/wgsl/">WGSL</a> source code for the shader
        module.

    : <dfn>compilationHints</dfn>
    ::
        A list of {{GPUShaderModuleCompilationHint}}s.

        Any hint provided by an application **should** contain information about one entry point of
        a pipeline that will eventually be created from the entry point.

        Implementations **should** use any information present in the {{GPUShaderModuleCompilationHint}}
        to perform as much compilation as is possible within {{GPUDevice/createShaderModule()}}.

        Aside from type-checking, these hints are not validated in any way.

        <div class=note heading>
            Supplying information in {{GPUShaderModuleDescriptor/compilationHints}} does not have any
            observable effect, other than performance. It may be detrimental to performance to
            provide hints for pipelines that never end up being created.

            Because a single shader module can hold multiple entry points, and multiple pipelines
            can be created from a single shader module, it can be more performant for an
            implementation to do as much compilation as possible once in
            {{GPUDevice/createShaderModule()}} rather than multiple times in the multiple calls to
            {{GPUDevice/createComputePipeline()}} or {{GPUDevice/createRenderPipeline()}}.

            Hints are only applied to the entry points they explicitly name.
            Unlike {{GPUProgrammableStage/entryPoint|GPUProgrammableStage.entryPoint}},
            there is no default, even if only one entry point is present in the module.
        </div>

        Note:
        Hints are not validated in an observable way, but user agents **may** surface identifiable
        errors (like unknown entry point names or incompatible pipeline layouts) to developers,
        for example in the browser developer console.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createShaderModule(descriptor)</dfn>
    ::
        Creates a {{GPUShaderModule}}.

        <div algorithm=GPUDevice.createShaderModule>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createShaderModule(descriptor)">
                    |descriptor|: Description of the {{GPUShaderModule}} to create.
                </pre>

                **Returns:** {{GPUShaderModule}}

                [=Content timeline=] steps:

                1. Let |sm| be [=!=] [$create a new WebGPU object$](|this|, {{GPUShaderModule}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |sm|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |error| be any error that results from [=shader module creation=] with the
                    WGSL source |descriptor|.{{GPUShaderModuleDescriptor/code}}, or `null` if no
                    errors occured.
                1. If any of the following requirements are unmet,
                    [$generate a validation error$], [$invalidate$] |sm|, and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - |error| must not be a [=shader-creation error|shader-creation=] [=program error=].
                        - For each `enable` extension in |descriptor|.{{GPUShaderModuleDescriptor/code}},
                            the corresponding {{GPUFeatureName}} must be enabled
                            (see the [[#feature-index|Feature Index]]).
                    </div>

                    Note: [=Uncategorized errors=] cannot arise from shader module creation.
                    Implementations which detect such errors during shader module creation
                    must behave as if the shader module is valid, and defer surfacing the
                    error until pipeline creation.

                <div class=note heading>
                    User agents **should not** include detailed compiler error messages or shader text in
                    the {{GPUError/message}} text of validation errors arising here:
                    these details are accessible via {{GPUShaderModule/getCompilationInfo()}}.
                    User agents **should** surface human-readable, formatted error details *to
                    developers* for easier debugging (for example as a warning in the browser developer
                    console, expandable to show full shader source).

                    As shader compilation errors should be rare in production applications, user agents
                    could choose to surface them *to developers* regardless of error handling ([=GPU error scopes=] or
                    {{GPUDevice/uncapturederror}} event handlers), e.g. as an expandable warning.
                    If not, they should provide and document another way for developers to access
                    human-readable error details, for example by adding a checkbox to show errors
                    unconditionally, or by showing human-readable details when logging a
                    {{GPUCompilationInfo}} object to the console.
                </div>
            </div>
        </div>
</dl>

<div class=example>
    Create a {{GPUShaderModule}} from WGSL code:

    <pre highlight=js>
        // A simple vertex and fragment shader pair that will fill the viewport with red.
        const shaderSource = \`
            var&lt;private&gt; pos : array&lt;vec2&lt;f32&gt;, 3&gt; = array&lt;vec2&lt;f32&gt;, 3&gt;(
                vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));

            @vertex
            fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -&gt; @builtin(position) vec4&lt;f32&gt; {
                return vec4(pos[vertexIndex], 1.0, 1.0);
            }

            @fragment
            fn fragmentMain() -&gt; @location(0) vec4&lt;f32&gt; {
                return vec4(1.0, 0.0, 0.0, 1.0);
            }
        \`;

        const shaderModule = gpuDevice.createShaderModule({
            code: shaderSource,
        });
    </pre>
</div>

#### Shader Module Compilation Hints #### {#shader-module-compilation-hints}

Shader module compilation hints are optional, additional information indicating how a given
{{GPUShaderModule}} entry point is intended to be used in the future. For some implementations this
information may aid in compiling the shader module earlier, potentially increasing performance.

<script type=idl>
dictionary GPUShaderModuleCompilationHint {
    required USVString entryPoint;
    (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUShaderModuleCompilationHint>
    : <dfn>layout</dfn>
    ::
        A {{GPUPipelineLayout}} that the {{GPUShaderModule}} may be used with in a future
        {{GPUDevice/createComputePipeline()}} or {{GPUDevice/createRenderPipeline()}} call.
        If set to {{GPUAutoLayoutMode/"auto"}} the layout will be the [$default pipeline layout$]
        for the entry point associated with this hint will be used.
</dl>

<div class=note heading>
    If possible, authors should be supplying the same information to
    {{GPUDevice/createShaderModule()}} and {{GPUDevice/createComputePipeline()}} /
    {{GPUDevice/createRenderPipeline()}}.

    If an application is unable to provide hint information at the time of calling
    {{GPUDevice/createShaderModule()}}, it should usually not delay calling
    {{GPUDevice/createShaderModule()}}, but instead just omit the unknown information from
    the {{GPUShaderModuleDescriptor/compilationHints}} sequence or the individual members of
    {{GPUShaderModuleCompilationHint}}. Omitting this information
    may cause compilation to be deferred to {{GPUDevice/createComputePipeline()}} /
    {{GPUDevice/createRenderPipeline()}}.

    If an author is not confident that the hint information passed to {{GPUDevice/createShaderModule()}}
    will match the information later passed to {{GPUDevice/createComputePipeline()}} /
    {{GPUDevice/createRenderPipeline()}} with that same module, they should avoid passing that
    information to {{GPUDevice/createShaderModule()}}, as passing mismatched information to
    {{GPUDevice/createShaderModule()}} may cause unnecessary compilations to occur.
</div>

### Shader Module Compilation Information ### {#shader-module-compilation-information}

<script type=idl>
enum GPUCompilationMessageType {
    "error",
    "warning",
    "info",
};

[Exposed=(Window, Worker), Serializable, SecureContext]
interface GPUCompilationMessage {
    readonly attribute DOMString message;
    readonly attribute GPUCompilationMessageType type;
    readonly attribute unsigned long long lineNum;
    readonly attribute unsigned long long linePos;
    readonly attribute unsigned long long offset;
    readonly attribute unsigned long long length;
};

[Exposed=(Window, Worker), Serializable, SecureContext]
interface GPUCompilationInfo {
    readonly attribute FrozenArray<GPUCompilationMessage> messages;
};
</script>

A {{GPUCompilationMessage}} is an informational, warning, or error message generated by the
{{GPUShaderModule}} compiler. The messages are intended to be human readable to help developers
diagnose issues with their shader {{GPUShaderModuleDescriptor/code}}. Each message may correspond to
either a single point in the shader code, a substring of the shader code, or may not correspond to
any specific point in the code at all.

{{GPUCompilationMessage}} has the following attributes:

<dl dfn-type=attribute dfn-for=GPUCompilationMessage data-timeline=content>
    : <dfn>message</dfn>
    ::
        The human-readable, [=localizable text=] for this compilation message.

        Note: The {{GPUCompilationMessage/message}} should follow the [=best practices for language
        and direction information=]. This includes making use of any future standards which may
        emerge regarding the reporting of string language and direction metadata.

        <p class="note editorial"><span class=marker>Editorial note:</span>
        At the time of this writing, no language/direction recommendation is available that provides
        compatibility and consistency with legacy APIs, but when there is, adopt it formally.

    : <dfn>type</dfn>
    ::
        The severity level of the message.

        If the {{GPUCompilationMessage/type}} is {{GPUCompilationMessageType/"error"}}, it
        corresponds to a [=shader-creation error=].

    : <dfn>lineNum</dfn>
    ::
        The line number in the shader {{GPUShaderModuleDescriptor/code}} the
        {{GPUCompilationMessage/message}} corresponds to. Value is one-based, such that a lineNum of
        `1` indicates the first line of the shader {{GPUShaderModuleDescriptor/code}}. Lines are
        delimited by [=line breaks=].

        If the {{GPUCompilationMessage/message}} corresponds to a substring this points to
        the line on which the substring begins. Must be `0` if the {{GPUCompilationMessage/message}}
        does not correspond to any specific point in the shader {{GPUShaderModuleDescriptor/code}}.

    : <dfn>linePos</dfn>
    ::
        The offset, in UTF-16 code units, from the beginning of line {{GPUCompilationMessage/lineNum}}
        of the shader {{GPUShaderModuleDescriptor/code}} to the point or beginning of the substring
        that the {{GPUCompilationMessage/message}} corresponds to. Value is one-based, such that a
        {{GPUCompilationMessage/linePos}} of `1` indicates the first code unit of the line.

        If {{GPUCompilationMessage/message}} corresponds to a substring this points to the
        first UTF-16 code unit of the substring. Must be `0` if the {{GPUCompilationMessage/message}}
        does not correspond to any specific point in the shader {{GPUShaderModuleDescriptor/code}}.

    : <dfn>offset</dfn>
    ::
        The offset from the beginning of the shader {{GPUShaderModuleDescriptor/code}} in UTF-16
        code units to the point or beginning of the substring that {{GPUCompilationMessage/message}}
        corresponds to. Must reference the same position as {{GPUCompilationMessage/lineNum}} and
        {{GPUCompilationMessage/linePos}}. Must be `0` if the {{GPUCompilationMessage/message}}
        does not correspond to any specific point in the shader {{GPUShaderModuleDescriptor/code}}.

    : <dfn>length</dfn>
    ::
        The number of UTF-16 code units in the substring that {{GPUCompilationMessage/message}}
        corresponds to. If the message does not correspond with a substring then
        {{GPUCompilationMessage/length}} must be 0.
</dl>

Note: {{GPUCompilationMessage}}.{{GPUCompilationMessage/lineNum}} and
{{GPUCompilationMessage}}.{{GPUCompilationMessage/linePos}} are one-based since the most common use
for them is expected to be printing human readable messages that can be correlated with the line and
column numbers shown in many text editors.

Note: {{GPUCompilationMessage}}.{{GPUCompilationMessage/offset}} and
{{GPUCompilationMessage}}.{{GPUCompilationMessage/length}} are appropriate to pass to
`substr()` in order to retrieve the substring of the shader {{GPUShaderModuleDescriptor/code}} the
{{GPUCompilationMessage/message}} corresponds to.

<dl dfn-type=method dfn-for=GPUShaderModule>
    : <dfn>getCompilationInfo()</dfn>
    ::
        Returns any messages generated during the {{GPUShaderModule}}'s compilation.

        The locations, order, and contents of messages are [=implementation-defined=]
        In particular, messages may not be ordered by {{GPUCompilationMessage/lineNum}}.

        <div algorithm=GPUShaderModule.getCompilationInfo>
            <div data-timeline=content>
                **Called on:** {{GPUShaderModule}} this

                **Returns:** {{Promise}}&lt;{{GPUCompilationInfo}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |synchronization steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |synchronization steps|:

                1. Let |event| occur upon the (successful or unsuccessful) completion of
                    [=shader module creation=] for |this|.
                1. [$Listen for timeline event$] |event|
                    on |this|.{{GPUObjectBase/[[device]]}}, handled by
                    the subsequent steps on <var data-timeline=content>contentTimeline</var>.
            </div>
            <div data-timeline=content>
                [=Content timeline=] steps:

                1. Let |info| be a new {{GPUCompilationInfo}}.
                1. Let |messages| be a list of any errors, warnings, or informational messages
                    generated during [=shader module creation=] for |this|, or the empty list
                    `[]` if the device was lost.
                1. For each |message| in |messages|:
                    1. Let |m| be a new {{GPUCompilationMessage}}.
                    1. Set |m|.{{GPUCompilationMessage/message}} to be the text of |message|.
                    1.
                        <dl class=switch>
                            : If |message| is a [=shader-creation error=]:
                            :: Set |m|.{{GPUCompilationMessage/type}} to
                                {{GPUCompilationMessageType/"error"}}
                            : If |message| is a warning:
                            :: Set |m|.{{GPUCompilationMessage/type}} to
                                {{GPUCompilationMessageType/"warning"}}
                            : Otherwise:
                            :: Set |m|.{{GPUCompilationMessage/type}} to
                                {{GPUCompilationMessageType/"info"}}
                        </dl>
                    1.
                        <dl class=switch>
                            : If |message| is associated with a specific substring or position
                                within the shader {{GPUShaderModuleDescriptor/code}}:
                            ::
                                1. Set |m|.{{GPUCompilationMessage/lineNum}} to the one-based number
                                    of the first line that the message refers to.
                                1. Set |m|.{{GPUCompilationMessage/linePos}} to the one-based number
                                    of the first UTF-16 code units on |m|.{{GPUCompilationMessage/lineNum}}
                                    that the message refers to, or `1` if the |message| refers to
                                    the entire line.
                                1. Set |m|.{{GPUCompilationMessage/offset}} to the number of UTF-16
                                    code units from the beginning of the shader to beginning of the
                                    substring or position that |message| refers to.
                                1. Set |m|.{{GPUCompilationMessage/length}} the length of the
                                    substring in UTF-16 code units that |message| refers to, or 0
                                    if |message| refers to a position
                            : Otherwise:
                            ::
                                1. Set |m|.{{GPUCompilationMessage/lineNum}} to `0`.
                                1. Set |m|.{{GPUCompilationMessage/linePos}} to `0`.
                                1. Set |m|.{{GPUCompilationMessage/offset}} to `0`.
                                1. Set |m|.{{GPUCompilationMessage/length}} to `0`.
                        </dl>
                    1. [=list/Append=] |m| to |info|.{{GPUCompilationInfo/messages}}.

                1. [=Resolve=] |promise| with |info|.
            </div>
        </div>
</dl>

# Pipelines # {#pipelines}

A <dfn dfn>pipeline</dfn>, be it {{GPUComputePipeline}} or {{GPURenderPipeline}},
represents the complete function done by a combination of the GPU hardware, the driver,
and the user agent, that process the input data in the shape of bindings and vertex buffers,
and produces some output, like the colors in the output render targets.

Structurally, the [=pipeline=] consists of a sequence of programmable stages (shaders)
and fixed-function states, such as the blending modes.

Note: Internally, depending on the target platform,
the driver may convert some of the fixed-function states into shader code,
and link it together with the shaders provided by the user.
This linking is one of the reason the object is created as a whole.

This combination state is created as a single object
(a {{GPUComputePipeline}} or {{GPURenderPipeline}})
and switched using one command
({{GPUComputePassEncoder}}.{{GPUComputePassEncoder/setPipeline()}} or
{{GPURenderCommandsMixin}}.{{GPURenderCommandsMixin/setPipeline()}} respectively).

There are two ways to create pipelines:

: <dfn dfn>immediate pipeline creation</dfn>
:: {{GPUDevice/createComputePipeline()}} and {{GPUDevice/createRenderPipeline()}}
    return a pipeline object which can be used immediately in a pass encoder.

    When this fails, the pipeline object will be invalid and the call will generate either a
    [$validation error$] or an [$internal error$].

    Note:
    A handle object is returned immediately, but actual pipeline creation is not synchronous.
    If pipeline creation takes a long time, this can incur a stall in the
    [=device timeline=] at some point between the creation call and execution of the
    {{GPUQueue/submit()}} in which it is first used.
    The point is unspecified, but most likely to be one of: at creation, at the first usage of the
    pipeline in `setPipeline()`, at the corresponding `finish()` of that {{GPUCommandEncoder}} or
    {{GPURenderBundleEncoder}}, or at {{GPUQueue/submit()}} of that {{GPUCommandBuffer}}.

: <dfn dfn>async pipeline creation</dfn>
:: {{GPUDevice/createComputePipelineAsync()}} and {{GPUDevice/createRenderPipelineAsync()}}
    return a `Promise` which resolves to a pipeline object when creation of the pipeline has
    completed.

    When this fails, the `Promise` rejects with a {{GPUPipelineError}}.

<dfn interface>GPUPipelineError</dfn> describes a pipeline creation failure.

<script type=idl>
[Exposed=(Window, Worker), SecureContext, Serializable]
interface GPUPipelineError : DOMException {
    constructor(optional DOMString message = "", GPUPipelineErrorInit options);
    readonly attribute GPUPipelineErrorReason reason;
};

dictionary GPUPipelineErrorInit {
    required GPUPipelineErrorReason reason;
};

enum GPUPipelineErrorReason {
    "validation",
    "internal",
};
</script>

{{GPUPipelineError}} constructor:

<dl dfn-type=constructor dfn-for=GPUPipelineError data-timeline=content>
    : <dfn>constructor()</dfn>
    ::
        <div algorithm="GPUPipelineError constructor">
            **Arguments:**

            <pre class=argumentdef for="GPUPipelineError/constructor()">
                |message|: Error message of the base {{DOMException}}.
                |options|: Options specific to {{GPUPipelineError}}.
            </pre>

            [=Content timeline=] steps:

            1. Set [=this=].[=DOMException/name=] to `"GPUPipelineError"`.
            1. Set [=this=].[=DOMException/message=] to |message|.
            1. Set [=this=].{{GPUPipelineError/reason}} to |options|.{{GPUPipelineErrorInit/reason}}.
        </div>
</dl>

{{GPUPipelineError}} has the following attributes:

<dl dfn-type=attribute dfn-for=GPUPipelineError data-timeline=content>
    : <dfn>reason</dfn>
    ::
        A read-only [=slot-backed attribute=] exposing the type of error encountered in pipeline creation
        as a <dfn enum for="">GPUPipelineErrorReason</dfn>:

        <ul dfn-type=enum-value dfn-for=GPUPipelineErrorReason>
            - <dfn>"validation"</dfn>: A [$validation error$].
            - <dfn>"internal"</dfn>: An [$internal error$].
        </ul>
</dl>

{{GPUPipelineError}} objects are [[HTML#serializable-objects|serializable objects]].

<div algorithm="GPUPipelineError serialization steps" data-timeline=content>
    Their [=serialization steps=], given |value| and |serialized|, are:

    1. Run the {{DOMException}} [=serialization steps=] given |value| and |serialized|.
</div>

<div algorithm="GPUPipelineError deserialization steps" data-timeline=content>
    Their [=deserialization steps=], given |value| and |serialized|, are:

    1. Run the {{DOMException}} [=deserialization steps=] given |value| and |serialized|.
</div>

## Base pipelines ## {#pipeline-base}

<script type=idl>
enum GPUAutoLayoutMode {
    "auto",
};

dictionary GPUPipelineDescriptorBase
         : GPUObjectDescriptorBase {
    required (GPUPipelineLayout or GPUAutoLayoutMode) layout;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUPipelineDescriptorBase>
    : <dfn>layout</dfn>
    ::
        The {{GPUPipelineLayout}} for this pipeline, or {{GPUAutoLayoutMode/"auto"}} to generate
        the pipeline layout automatically.

        Note: If {{GPUAutoLayoutMode/"auto"}} is used the pipeline cannot share {{GPUBindGroup}}s
        with any other pipelines.
</dl>

<script type=idl>
interface mixin GPUPipelineBase {
    [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index);
};
</script>

{{GPUPipelineBase}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUPipelineBase data-timeline=device>
    : <dfn>\[[layout]]</dfn>, of type `GPUPipelineLayout`
    ::
        The definition of the layout of resources which can be used with `this`.
</dl>

{{GPUPipelineBase}} has the following methods:

<dl dfn-type=method dfn-for=GPUPipelineBase>
    : <dfn>getBindGroupLayout(index)</dfn>
    ::
        Gets a {{GPUBindGroupLayout}} that is compatible with the {{GPUPipelineBase}}'s
        {{GPUBindGroupLayout}} at `index`.

        <div algorithm=GPUPipelineBase.getBindGroupLayout>
            <div data-timeline=content>
                **Called on:** {{GPUPipelineBase}} |this|

                **Arguments:**

                <pre class=argumentdef for="GPUPipelineBase/getBindGroupLayout(index)">
                    |index|: Index into the pipeline layout's {{GPUPipelineLayout/[[bindGroupLayouts]]}}
                        sequence.
                </pre>

                **Returns:** {{GPUBindGroupLayout}}

                [=Content timeline=] steps:

                1. Let |layout| be a new {{GPUBindGroupLayout}} object.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |layout|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |limits| be |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.
                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |layout| and return.

                    <div class=validusage>
                        - |this| must be [$valid$].
                        - |index| &lt; |limits|.{{supported limits/maxBindGroups}}.
                    </div>

                1. Initialize |layout| so it is a copy of
                    |this|.{{GPUPipelineBase/[[layout]]}}.{{GPUPipelineLayout/[[bindGroupLayouts]]}}[|index|].

                    Note: {{GPUBindGroupLayout}} is only ever used by-value, not by-reference,
                    so this is equivalent to returning the same [=internal object=] with a new [=WebGPU interface=].
                    A new {{GPUBindGroupLayout}} [=WebGPU interface=] is returned each time to avoid a round-trip
                    between the [=Content timeline=] and the [=Device timeline=].
            </div>
        </div>
</dl>

### Default pipeline layout ### {#default-pipeline-layout}

A {{GPUPipelineBase}} object that was created with a {{GPUPipelineDescriptorBase/layout}} set to
{{GPUAutoLayoutMode/"auto"}} has a default layout created and used instead.

Note: Default layouts are provided as a convenience for simple pipelines, but use of explicit layouts
is recommended in most cases. Bind groups created from default layouts cannot be used with other
pipelines, and the structure of the default layout may change when altering shaders, causing
unexpected bind group creation errors.

<div algorithm="default pipeline layout creation" data-timeline=device>

To create a <dfn abstract-op>default pipeline layout</dfn> for {{GPUPipelineBase}} |pipeline|,
run the following [=device timeline=] steps:

    1. Let |groupCount| be 0.
    1. Let |groupDescs| be a sequence of |device|.{{device/[[limits]]}}.{{supported limits/maxBindGroups}}
        new {{GPUBindGroupLayoutDescriptor}} objects.
    1. For each |groupDesc| in |groupDescs|:

        1. Set |groupDesc|.{{GPUBindGroupLayoutDescriptor/entries}} to an empty [=sequence=].

    1. For each {{GPUProgrammableStage}} |stageDesc| in the descriptor used to create |pipeline|:

        1. Let |shaderStage| be the {{GPUShaderStageFlags}} for the shader stage
            at which |stageDesc| is used in |pipeline|.

        1. Let |entryPoint| be [$get the entry point$](|shaderStage|, |stageDesc|). [=Assert=] |entryPoint| is not `null`.

        1. For each resource |resource| [=statically used=] by |entryPoint|:

            1. Let |group| be |resource|'s "group" decoration.
            1. Let |binding| be |resource|'s "binding" decoration.
            1. Let |entry| be a new {{GPUBindGroupLayoutEntry}}.
            1. Set |entry|.{{GPUBindGroupLayoutEntry/binding}} to |binding|.
            1. Set |entry|.{{GPUBindGroupLayoutEntry/visibility}} to |shaderStage|.
            1. If |resource| is for a sampler binding:

                1. Let |samplerLayout| be a new {{GPUSamplerBindingLayout}}.
                1. Set |entry|.{{GPUBindGroupLayoutEntry/sampler}} to |samplerLayout|.

            1. If |resource| is for a comparison sampler binding:

                1. Let |samplerLayout| be a new {{GPUSamplerBindingLayout}}.
                1. Set |samplerLayout|.{{GPUSamplerBindingLayout/type}} to {{GPUSamplerBindingType/"comparison"}}.
                1. Set |entry|.{{GPUBindGroupLayoutEntry/sampler}} to |samplerLayout|.

            1. If |resource| is for a buffer binding:

                1. Let |bufferLayout| be a new {{GPUBufferBindingLayout}}.

                1. Set |bufferLayout|.{{GPUBufferBindingLayout/minBindingSize}} to |resource|'s [=minimum buffer binding size=].

                1. If |resource| is for a read-only storage buffer:

                    1. Set |bufferLayout|.{{GPUBufferBindingLayout/type}} to {{GPUBufferBindingType/"read-only-storage"}}.

                1. If |resource| is for a storage buffer:

                    1. Set |bufferLayout|.{{GPUBufferBindingLayout/type}} to {{GPUBufferBindingType/"storage"}}.

                1. Set |entry|.{{GPUBindGroupLayoutEntry/buffer}} to |bufferLayout|.

            1. If |resource| is for a sampled texture binding:

                1. Let |textureLayout| be a new {{GPUTextureBindingLayout}}.

                1. If |resource| is a depth texture binding:

                    - Set |textureLayout|.{{GPUTextureBindingLayout/sampleType}} to {{GPUTextureSampleType/"depth"}}

                    Otherwise, if the sampled type of |resource| is:

                    <dl class=switch>
                        : `f32` and there exists a [=static use=] of |resource| by |stageDesc| in a texture builtin function call that also uses a sampler
                        :: Set |textureLayout|.{{GPUTextureBindingLayout/sampleType}} to {{GPUTextureSampleType/"float"}}
                        : `f32` otherwise
                        :: Set |textureLayout|.{{GPUTextureBindingLayout/sampleType}} to {{GPUTextureSampleType/"unfilterable-float"}}
                        : `i32`
                        :: Set |textureLayout|.{{GPUTextureBindingLayout/sampleType}} to {{GPUTextureSampleType/"sint"}}
                        : `u32`
                        :: Set |textureLayout|.{{GPUTextureBindingLayout/sampleType}} to {{GPUTextureSampleType/"uint"}}
                    </dl>

                1. Set |textureLayout|.{{GPUTextureBindingLayout/viewDimension}} to |resource|'s dimension.
                1. If |resource| is for a multisampled texture:

                    1. Set |textureLayout|.{{GPUTextureBindingLayout/multisampled}} to `true`.

                1. Set |entry|.{{GPUBindGroupLayoutEntry/texture}} to |textureLayout|.

            1. If |resource| is for a storage texture binding:

                1. Let |storageTextureLayout| be a new {{GPUStorageTextureBindingLayout}}.
                1. Set |storageTextureLayout|.{{GPUStorageTextureBindingLayout/format}} to |resource|'s format.
                1. Set |storageTextureLayout|.{{GPUStorageTextureBindingLayout/viewDimension}} to |resource|'s dimension.

                1. If the access mode is:

                    <dl class=switch>
                        : `read`
                        :: Set |textureLayout|.{{GPUStorageTextureBindingLayout/access}} to {{GPUStorageTextureAccess/"read-only"}}.
                        : `write`
                        :: Set |textureLayout|.{{GPUStorageTextureBindingLayout/access}} to {{GPUStorageTextureAccess/"write-only"}}.
                        : `read_write`
                        :: Set |textureLayout|.{{GPUStorageTextureBindingLayout/access}} to {{GPUStorageTextureAccess/"read-write"}}.
                    </dl>

                1. Set |entry|.{{GPUBindGroupLayoutEntry/storageTexture}} to |storageTextureLayout|.

            1. Set |groupCount| to max(|groupCount|, |group| + 1).

            1. If |groupDescs|[|group|] has an entry |previousEntry| with {{GPUBindGroupLayoutEntry/binding}} equal to |binding|:

                1. If |entry| has different {{GPUBindGroupLayoutEntry/visibility}} than |previousEntry|:

                    1. Add the bits set in |entry|.{{GPUBindGroupLayoutEntry/visibility}} into |previousEntry|.{{GPUBindGroupLayoutEntry/visibility}}

                1. If |resource| is for a buffer binding and |entry| has greater
                    {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}
                    than |previousEntry|:

                    1. Set |previousEntry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}
                        to |entry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}}.

                1. If |resource| is a sampled texture binding and |entry| has different
                    {{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}} than |previousEntry|
                    and both |entry| and |previousEntry| have {{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}}
                    of either {{GPUTextureSampleType/"float"}} or {{GPUTextureSampleType/"unfilterable-float"}}:
                    1. Set |previousEntry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}} to
                        {{GPUTextureSampleType/"float"}}.

                1. If any other property is unequal between |entry| and |previousEntry|:

                    1. Return `null` (which will cause the creation of the pipeline to fail).

                1. If |resource| is a storage texture binding,
                    |entry|.|storageTexture|.{{GPUStorageTextureBindingLayout/access}} is {{GPUStorageTextureAccess/"read-write"}},
                    |previousEntry|.|storageTexture|.{{GPUStorageTextureBindingLayout/access}} is {{GPUStorageTextureAccess/"write-only"}}, and
                    |previousEntry|.|storageTexture|.{{GPUStorageTextureBindingLayout/format}} is compatible with
                    {{GPUTextureUsage/STORAGE_BINDING}} and {{GPUStorageTextureAccess/"read-write"}} according to the [[#plain-color-formats]] table:

                    1. Set |previousEntry|.|storageTexture|.{{GPUStorageTextureBindingLayout/access}} to {{GPUStorageTextureAccess/"read-write"}}.

                    Otherwise:

                    1. Append |entry| to |groupDescs|[|group|].

    1. Let |groupLayouts| be a new [=list=].
    1. For each |i| from 0 to |groupCount| - 1, inclusive:
        1. Let |groupDesc| be |groupDescs|[|i|].
        1. Let |bindGroupLayout| be the result of calling |device|.{{GPUDevice/createBindGroupLayout()}}(|groupDesc|).
        1. Set |bindGroupLayout|.{{GPUBindGroupLayout/[[exclusivePipeline]]}} to |pipeline|.
        1. Append |bindGroupLayout| to |groupLayouts|.

    1. Let |desc| be a new {{GPUPipelineLayoutDescriptor}}.
    1. Set |desc|.{{GPUPipelineLayoutDescriptor/bindGroupLayouts}} to |groupLayouts|.
    1. Return |device|.{{GPUDevice/createPipelineLayout()}}(|desc|).
</div>

<h4 id=gpuprogrammablestage data-dfn-type=dictionary>`GPUProgrammableStage`
<span id=GPUProgrammableStage></span>
</h4>

A {{GPUProgrammableStage}} describes the entry point in the user-provided
{{GPUShaderModule}} that controls one of the programmable stages of a [=pipeline=].
Entry point names follow the rules defined in [=WGSL identifier comparison=].

<script type=idl>
dictionary GPUProgrammableStage {
    required GPUShaderModule module;
    USVString entryPoint;
    record<USVString, GPUPipelineConstantValue> constants = {};
};

typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, u32, and f16 if enabled.
</script>

{{GPUProgrammableStage}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUProgrammableStage>
    : <dfn>module</dfn>
    ::
        The {{GPUShaderModule}} containing the code that this programmable stage will execute.

    : <dfn>entryPoint</dfn>
    ::
        The name of the function in {{GPUProgrammableStage/module}} that this stage will use to
        perform its work.

        NOTE: Since the {{GPUProgrammableStage/entryPoint}} dictionary member is
        not required, methods which consume a {{GPUProgrammableStage}} must use the
        "[$get the entry point$]" algorithm to determine which entry point
        it refers to.

    : <dfn>constants</dfn>
    ::
        Specifies the values of [=pipeline-overridable=] constants in the shader module
        {{GPUProgrammableStage/module}}.

        Each such [=pipeline-overridable=] constant is uniquely identified by a single
        [=pipeline-overridable constant identifier string=], representing the [=pipeline
        constant ID=] of the constant if its declaration specifies one, and otherwise the
        constant's identifier name.

        The key of each key-value pair must equal the
        [=pipeline-overridable constant identifier string|identifier string=]
        of one such constant, with the comparison performed
        according to the rules for [=WGSL identifier comparison=].
        When the pipeline is executed, that constant will have the specified value.

        Values are specified as <dfn typedef for="">GPUPipelineConstantValue</dfn>, which is a {{double}}.
        They are converted [$to WGSL type$] of the pipeline-overridable constant (`bool`/`i32`/`u32`/`f32`/`f16`).
        If conversion fails, a validation error is generated.

        <div class=example>
            Pipeline-overridable constants defined in WGSL:

            <pre highlight=wgsl>
                @id(0)      override has_point_light: bool = true;  // Algorithmic control.
                @id(1200)   override specular_param: f32 = 2.3;     // Numeric control.
                @id(1300)   override gain: f32;                     // Must be overridden.
                            override width: f32 = 0.0;              // Specifed at the API level
                                                                    //   using the name "width".
                            override depth: f32;                    // Specifed at the API level
                                                                    //   using the name "depth".
                                                                    //   Must be overridden.
                            override height = 2 * depth;            // The default value
                                                                    // (if not set at the API level),
                                                                    // depends on another
                                                                    // overridable constant.
            </pre>

            Corresponding JavaScript code, providing only the overrides which are required
            (have no defaults):

            <pre highlight=js>
                {
                    // ...
                    constants: {
                        1300: 2.0,  // "gain"
                        depth: -1,  // "depth"
                    }
                }
            </pre>

            Corresponding JavaScript code, overriding all constants:

            <pre highlight=js>
                {
                    // ...
                    constants: {
                        0: false,   // "has_point_light"
                        1200: 3.0,  // "specular_param"
                        1300: 2.0,  // "gain"
                        width: 20,  // "width"
                        depth: -1,  // "depth"
                        height: 15, // "height"
                    }
                }
            </pre>
        </div>
</dl>

<div algorithm data-timeline=device>
    To <dfn abstract-op>get the entry point</dfn>({{GPUShaderStage}} |stage|,
    {{GPUProgrammableStage}} |descriptor|), run the following [=device timeline=] steps:

    1. If |descriptor|.{{GPUProgrammableStage/entryPoint}} is [=map/exists|provided=]:

        1. If |descriptor|.{{GPUProgrammableStage/module}} contains an entry point
            whose name equals |descriptor|.{{GPUProgrammableStage/entryPoint}},
            and whose shader stage equals |stage|,
            return that entry point.

            Otherwise, return `null`.

        Otherwise:

        1. If there is exactly one entry point in |descriptor|.{{GPUProgrammableStage/module}}
            whose shader stage equals |stage|, return that entry point.

            Otherwise, return `null`.
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUProgrammableStage</dfn>(|stage|, |descriptor|, |layout|, |device|)

    **Arguments:**

    - {{GPUShaderStage}} |stage|
    - {{GPUProgrammableStage}} |descriptor|
    - {{GPUPipelineLayout}} |layout|
    - {{GPUDevice}} |device|

    All of the requirements in the following steps |must| be met.
    If any are unmet, return `false`; otherwise, return `true`.

    1. |descriptor|.{{GPUProgrammableStage/module}} |must| be [$valid to use with$] |device|.
    1. Let |entryPoint| be [$get the entry point$](|stage|, |descriptor|).
    1. |entryPoint| |must| not be `null`.
    1. For each |binding| that is [=statically used=] by |entryPoint|:
        - [$validating shader binding$](|binding|, |layout|) |must| return `true`.
    1. For each texture builtin function call in any of the [=functions in the shader stage=] rooted at |entryPoint|,
        if it uses a |textureBinding| of [=type/sampled texture=] or [=type/depth texture=] type
        together with a |samplerBinding| of `sampler` type (excluding `sampler_comparison`):
        1. Let |texture| be the {{GPUBindGroupLayoutEntry}} corresponding to |textureBinding|.
        1. Let |sampler| be the {{GPUBindGroupLayoutEntry}} corresponding to |samplerBinding|.
        1. If |sampler|.{{GPUSamplerBindingLayout/type}} is {{GPUSamplerBindingType/"filtering"}},
            then |texture|.{{GPUTextureBindingLayout/sampleType}} |must| be
            {{GPUTextureSampleType/"float"}}.

        Note: {{GPUSamplerBindingType/"comparison"}} samplers can also only be used with
        {{GPUTextureSampleType/"depth"}} textures, because they are the only texture type that can
        be bound to WGSL `texture_depth_*` bindings.
    1. For each |key| &rarr; |value| in |descriptor|.{{GPUProgrammableStage/constants}}:
        1. |key| |must| equal the [=pipeline-overridable constant identifier string=] of
            some [=pipeline-overridable=] constant defined in the shader module
            |descriptor|.{{GPUProgrammableStage/module}} by the rules defined in [=WGSL identifier comparison=].
            The pipeline-overridable constant is *not* required to be [=statically used=] by |entryPoint|.
            Let the type of that constant be |T|.
        1. Converting the IDL value |value| [$to WGSL type$] |T| |must| not throw a {{TypeError}}.
    1. For each [=pipeline-overridable constant identifier string=] |key| which is
        [=statically used=] by |entryPoint|:
        - If the pipeline-overridable constant identified by |key|
            [=pipeline-overridable constant default value|does not have a default value=],
            |descriptor|.{{GPUProgrammableStage/constants}} |must| [=map/contain=] |key|.
    1. [=pipeline-creation error|Pipeline-creation=] [=program errors=] |must| not
        result from the rules of the [[WGSL]] specification.
</div>

<div algorithm data-timeline=const>
    <dfn abstract-op>validating shader binding</dfn>(|variable|, |layout|)

    **Arguments:**

    - shader binding declaration |variable|, a module-scope variable declaration reflected from a shader module
    - {{GPUPipelineLayout}} |layout|

    Let |bindGroup| be the bind group index, and |bindIndex| be the binding index,
    of the shader binding declaration |variable|.

    Return `true` if all of the following conditions are satisfied:

        - |layout|.{{GPUPipelineLayout/[[bindGroupLayouts]]}}[|bindGroup|] contains
            a {{GPUBindGroupLayoutEntry}} |entry| whose |entry|.{{GPUBindGroupLayoutEntry/binding}} == |bindIndex|.
        - If the defined [=binding member=] for |entry| is:

            <dl class=switch>
                : {{GPUBindGroupLayoutEntry/buffer}}
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} is:

                    <dl class=switch>
                        : {{GPUBufferBindingType/"uniform"}}
                        :: |variable| is declared with address space `uniform`.
                        : {{GPUBufferBindingType/"storage"}}
                        :: |variable| is declared with address space `storage` and access mode `read_write`.
                        : {{GPUBufferBindingType/"read-only-storage"}}
                        :: |variable| is declared with address space `storage` and access mode `read`.
                    </dl>

                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/minBindingSize}} is not `0`,
                    then it must be at least the [=minimum buffer binding size=] for the associated
                    buffer binding variable in the shader.

                : {{GPUBindGroupLayoutEntry/sampler}}
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/sampler}}.{{GPUSamplerBindingLayout/type}} is:

                    <dl class=switch>
                        : {{GPUSamplerBindingType/"filtering"}} or {{GPUSamplerBindingType/"non-filtering"}}
                        :: |variable| has type `sampler`.
                        : {{GPUSamplerBindingType/"comparison"}}
                        :: |variable| has type `sampler_comparison`.
                    </dl>

                : {{GPUBindGroupLayoutEntry/texture}}
                ::
                    If, and only if,
                    |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/multisampled}}
                    is `true`, |variable| has type `texture_multisampled_2d<T>` or `texture_depth_multisampled_2d<T>`.
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}} is:

                    <dl class=switch>
                        : {{GPUTextureSampleType/"float"}}, {{GPUTextureSampleType/"unfilterable-float"}},
                            {{GPUTextureSampleType/"sint"}} or {{GPUTextureSampleType/"uint"}}
                        ::
                            |variable| has one of the types:

                            - `texture_1d<T>`
                            - `texture_2d<T>`
                            - `texture_2d_array<T>`
                            - `texture_cube<T>`
                            - `texture_cube_array<T>`
                            - `texture_3d<T>`
                            - `texture_multisampled_2d<T>`
                        ::
                            If |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/sampleType}} is:

                            <dl class=switch>
                                : {{GPUTextureSampleType/"float"}} or {{GPUTextureSampleType/"unfilterable-float"}}
                                :: The sampled type `T` is `f32`.
                                : {{GPUTextureSampleType/"sint"}}
                                :: The sampled type `T` is `i32`.
                                : {{GPUTextureSampleType/"uint"}}
                                :: The sampled type `T` is `u32`.
                            </dl>

                        : {{GPUTextureSampleType/"depth"}}
                        ::
                            |variable| has one of the types:

                            - `texture_2d<T>`
                            - `texture_2d_array<T>`
                            - `texture_cube<T>`
                            - `texture_cube_array<T>`
                            - `texture_multisampled_2d<T>`
                            - `texture_depth_2d`
                            - `texture_depth_2d_array`
                            - `texture_depth_cube`
                            - `texture_depth_cube_array`
                            - `texture_depth_multisampled_2d`

                            where the sampled type `T` is `f32`.
                    </dl>
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/texture}}.{{GPUTextureBindingLayout/viewDimension}} is:

                    <dl class=switch>
                        : {{GPUTextureViewDimension/"1d"}}
                        :: |variable| has type `texture_1d<T>`.
                        : {{GPUTextureViewDimension/"2d"}}
                        :: |variable| has type `texture_2d<T>` or `texture_multisampled_2d<T>`.
                        : {{GPUTextureViewDimension/"2d-array"}}
                        :: |variable| has type `texture_2d_array<T>`.
                        : {{GPUTextureViewDimension/"cube"}}
                        :: |variable| has type `texture_cube<T>`.
                        : {{GPUTextureViewDimension/"cube-array"}}
                        :: |variable| has type `texture_cube_array<T>`.
                        : {{GPUTextureViewDimension/"3d"}}
                        :: |variable| has type `texture_3d<T>`.
                    </dl>

                : {{GPUBindGroupLayoutEntry/storageTexture}}
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/viewDimension}} is:

                    <dl class=switch>
                        : {{GPUTextureViewDimension/"1d"}}
                        :: |variable| has type `texture_storage_1d<T, A>`.
                        : {{GPUTextureViewDimension/"2d"}}
                        :: |variable| has type `texture_storage_2d<T, A>`.
                        : {{GPUTextureViewDimension/"2d-array"}}
                        :: |variable| has type `texture_storage_2d_array<T, A>`.
                        : {{GPUTextureViewDimension/"3d"}}
                        :: |variable| has type `texture_storage_3d<T, A>`.
                    </dl>
                ::
                    If |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/access}} is:

                    <dl class=switch>
                        : {{GPUStorageTextureAccess/"write-only"}}
                        :: The access mode `A` is `write`.
                        : {{GPUStorageTextureAccess/"read-only"}}
                        :: The access mode `A` is `read`.
                        : {{GPUStorageTextureAccess/"read-write"}}
                        :: The access mode `A` is `read_write` or `write`.
                    </dl>
                ::
                    The texel format `T` equals
                    |entry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/format}}.
            </dl>
</div>

<div algorithm data-timeline=const>
    The <dfn dfn>minimum buffer binding size</dfn> for a buffer binding variable |var| is computed as follows:

    1. Let |T| be the [=store type=] of |var|.
    1. If |T| is a [=runtime-sized=] array, or contains a runtime-sized array, replace
        that `array<E>` with `array<E, 1>`.

        Note: This ensures there's always enough memory for one element, which allows array
        indices to be clamped to the length of the array resulting in an in-memory access.
    1. Return [$SizeOf$](|T|).

    Note:
    Enforcing this lower bound ensures reads and writes via the buffer variable only access memory locations
    within the bound region of the buffer.
</div>

<div algorithm data-timeline=const>
    A resource binding, [=pipeline-overridable=] constant, shader stage input, or shader stage output
    is considered to be <dfn dfn lt="statically used|static use">statically used</dfn>
    by an entry point if it is present in the [=interface of a shader
    stage|interface of the shader stage=] for that entry point.
</div>

<h3 id=gpucomputepipeline data-dfn-type=interface>`GPUComputePipeline`
<span id=compute-pipeline></span>
</h3>

A {{GPUComputePipeline}} is a kind of [=pipeline=] that controls the compute shader stage,
and can be used in {{GPUComputePassEncoder}}.

Compute inputs and outputs are all contained in the bindings,
according to the given {{GPUPipelineLayout}}.
The outputs correspond to {{GPUBindGroupLayoutEntry/buffer}} bindings with a type of {{GPUBufferBindingType/"storage"}}
and {{GPUBindGroupLayoutEntry/storageTexture}} bindings with a type of
{{GPUStorageTextureAccess/"write-only"}} or
{{GPUStorageTextureAccess/"read-write"}}.

Stages of a compute [=pipeline=]:

1. Compute shader

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUComputePipeline {
};
GPUComputePipeline includes GPUObjectBase;
GPUComputePipeline includes GPUPipelineBase;
</script>

### Compute Pipeline Creation ### {#compute-pipeline-creation}

A {{GPUComputePipelineDescriptor}} describes a compute [=pipeline=]. See
[[#computing-operations]] for additional details.

<script type=idl>
dictionary GPUComputePipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUProgrammableStage compute;
};
</script>

{{GPUComputePipelineDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUComputePipelineDescriptor>
    : <dfn>compute</dfn>
    ::
        Describes the compute shader entry point of the [=pipeline=].
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createComputePipeline(descriptor)</dfn>
    ::
        Creates a {{GPUComputePipeline}} using [=immediate pipeline creation=].

        <div algorithm=GPUDevice.createComputePipeline>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createComputePipeline(descriptor)">
                    |descriptor|: Description of the {{GPUComputePipeline}} to create.
                </pre>

                **Returns:** {{GPUComputePipeline}}

                [=Content timeline=] steps:

                1. Let |pipeline| be [=!=] [$create a new WebGPU object$](|this|, {{GPUComputePipeline}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |pipeline|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |layout| be a new [$default pipeline layout$] for |pipeline| if
                    |descriptor|.{{GPUPipelineDescriptorBase/layout}} is {{GPUAutoLayoutMode/"auto"}},
                    and |descriptor|.{{GPUPipelineDescriptorBase/layout}} otherwise.

                1. All of the requirements in the following steps |must| be met.
                    If any are unmet, [$generate a validation error$], [$invalidate$] |pipeline| and return.

                    <div class=validusage>
                        1. |layout| |must| be [$valid to use with$] |this|.
                        1. [$validating GPUProgrammableStage$]({{GPUShaderStage/COMPUTE}},
                            |descriptor|.{{GPUComputePipelineDescriptor/compute}}, |layout|, |this|) |must| succeed.
                        1. Let |entryPoint| be [$get the entry point$]({{GPUShaderStage/COMPUTE}}, |descriptor|.{{GPUComputePipelineDescriptor/compute}}).

                            [=Assert=] |entryPoint| is not `null`.
                        1. Let |workgroupStorageUsed| be the sum of [=roundUp=](16, [$SizeOf$](|T|)) over each
                            type |T| of all variables with address space "[=address spaces/workgroup=]"
                            [=statically used=] by |entryPoint|.

                            |workgroupStorageUsed| |must| be &le;
                            |device|.limits.{{supported limits/maxComputeWorkgroupStorageSize}}.
                        1. |entryPoint| |must| use &le;
                            |device|.limits.{{supported limits/maxComputeInvocationsPerWorkgroup}} per
                            workgroup.
                        1. Each component of |entryPoint|'s
                            `workgroup_size` attribute |must| be &le; the corresponding component in
                            [|device|.limits.{{supported limits/maxComputeWorkgroupSizeX}},
                            |device|.limits.{{supported limits/maxComputeWorkgroupSizeY}},
                            |device|.limits.{{supported limits/maxComputeWorkgroupSizeZ}}].
                    </div>

                1. If any [=pipeline-creation error|pipeline-creation=] [=uncategorized errors=]
                    result from the implementation of pipeline creation,
                    [$generate an internal error$], [$invalidate$] |pipeline| and return.

                    Note:
                    Even if the implementation detected [=uncategorized errors=] in shader module
                    creation, the error is surfaced here.

                1. Set |pipeline|.{{GPUPipelineBase/[[layout]]}} to |layout|.
            </div>
        </div>

    : <dfn>createComputePipelineAsync(descriptor)</dfn>
    ::
        Creates a {{GPUComputePipeline}} using [=async pipeline creation=].
        The returned {{Promise}} resolves when the created pipeline
        is ready to be used without additional delay.

        If pipeline creation fails, the returned {{Promise}} rejects with an {{GPUPipelineError}}.
        (A {{GPUError}} is not dispatched to the device.)

        Note: Use of this method is preferred whenever possible, as it prevents blocking the
        [=queue timeline=] work on pipeline compilation.

        <div algorithm=GPUDevice.createComputePipelineAsync>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createComputePipelineAsync(descriptor)">
                    |descriptor|: Description of the {{GPUComputePipeline}} to create.
                </pre>

                **Returns:** {{Promise}}&lt;{{GPUComputePipeline}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |pipeline| be a new {{GPUComputePipeline}} created as if
                    |this|.{{GPUDevice/createComputePipeline()}} was called with |descriptor|,
                    except capturing any errors as |error|, rather than dispatching them to the device.
                1. Let |event| occur upon the (successful or unsuccessful) completion of
                    [=pipeline creation=] for |pipeline|.
                1. [$Listen for timeline event$] |event|
                    on |this|.{{GPUObjectBase/[[device]]}}, handled by
                    the subsequent steps on the [=device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If |pipeline| is [$valid$] or |this| is [$invalid|lost$]:

                    1. Issue the following steps on |contentTimeline|:

                        <div data-timeline=content>
                            [=Content timeline=] steps:

                            1. [=Resolve=] |promise| with |pipeline|.
                        </div>

                    1. Return.

                    Note: No errors are generated from a device which is lost.
                    See [[#errors-and-debugging]].

                1. If |pipeline| is [$invalid$] and |error| is an [$internal error$],
                    issue the following steps on |contentTimeline|, and return.

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with a {{GPUPipelineError}} with
                            {{GPUPipelineErrorInit/reason}} {{GPUPipelineErrorReason/"internal"}}.
                    </div>

                1. If |pipeline| is [$invalid$] and |error| is a [$validation error$],
                    issue the following steps on |contentTimeline|, and return.

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with a {{GPUPipelineError}} with
                            {{GPUPipelineErrorInit/reason}} {{GPUPipelineErrorReason/"validation"}}.
                    </div>
            </div>
        </div>
</dl>

<div class=example>
    Creating a simple {{GPUComputePipeline}}:

    <pre highlight=js>
        const computePipeline = gpuDevice.createComputePipeline({
            layout: pipelineLayout,
            compute: {
                module: computeShaderModule,
                entryPoint: 'computeMain',
            }
        });
    </pre>
</div>

<h3 id=gpurenderpipeline data-dfn-type=interface>`GPURenderPipeline`
<span id=render-pipeline></span>
</h3>

A {{GPURenderPipeline}} is a kind of [=pipeline=] that controls the vertex
and fragment shader stages, and can be used in {{GPURenderPassEncoder}}
as well as {{GPURenderBundleEncoder}}.

Render [=pipeline=] inputs are:

- bindings, according to the given {{GPUPipelineLayout}}
- vertex and index buffers, described by {{GPUVertexState}}
- the color attachments, described by {{GPUColorTargetState}}
- optionally, the depth-stencil attachment, described by {{GPUDepthStencilState}}

Render [=pipeline=] outputs are:

- {{GPUBindGroupLayoutEntry/buffer}} bindings with a {{GPUBufferBindingLayout/type}} of {{GPUBufferBindingType/"storage"}}
- {{GPUBindGroupLayoutEntry/storageTexture}} bindings with a {{GPUStorageTextureBindingLayout/access}} of {{GPUStorageTextureAccess/"write-only"}} or {{GPUStorageTextureAccess/"read-write"}}
- the color attachments, described by {{GPUColorTargetState}}
- optionally, depth-stencil attachment, described by {{GPUDepthStencilState}}

A render [=pipeline=] is comprised of the following <dfn dfn>render stages</dfn>:

1. Vertex fetch, controlled by {{GPUVertexState/buffers|GPUVertexState.buffers}}
1. Vertex shader, controlled by {{GPUVertexState}}
1. Primitive assembly, controlled by {{GPUPrimitiveState}}
1. Rasterization, controlled by {{GPUPrimitiveState}}, {{GPUDepthStencilState}}, and {{GPUMultisampleState}}
1. Fragment shader, controlled by {{GPUFragmentState}}
1. Stencil test and operation, controlled by {{GPUDepthStencilState}}
1. Depth test and write, controlled by {{GPUDepthStencilState}}
1. Output merging, controlled by {{GPUFragmentState/targets|GPUFragmentState.targets}}

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPURenderPipeline {
};
GPURenderPipeline includes GPUObjectBase;
GPURenderPipeline includes GPUPipelineBase;
</script>

{{GPURenderPipeline}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPURenderPipeline data-timeline=device>
    : <dfn>\[[descriptor]]</dfn>, of type {{GPURenderPipelineDescriptor}}, readonly
    ::
        The {{GPURenderPipelineDescriptor}} describing this pipeline.

        All optional fields of {{GPURenderPipelineDescriptor}} are defined.

    : <dfn>\[[writesDepth]]</dfn>, of type {{boolean}}, readonly
    :: True if the pipeline writes to the depth component of the depth/stencil attachment

    : <dfn>\[[writesStencil]]</dfn>, of type {{boolean}}, readonly
    :: True if the pipeline writes to the stencil component of the depth/stencil attachment
</dl>

### Render Pipeline Creation ### {#render-pipeline-creation}

A {{GPURenderPipelineDescriptor}} describes a render [=pipeline=] by configuring each
of the [=render stages=]. See [[#rendering-operations]] for additional details.

<script type=idl>
dictionary GPURenderPipelineDescriptor
         : GPUPipelineDescriptorBase {
    required GPUVertexState vertex;
    GPUPrimitiveState primitive = {};
    GPUDepthStencilState depthStencil;
    GPUMultisampleState multisample = {};
    GPUFragmentState fragment;
};
</script>

{{GPURenderPipelineDescriptor}} has the following members:

<dl dfn-type=dict-member dfn-for=GPURenderPipelineDescriptor>
    : <dfn>vertex</dfn>
    ::
        Describes the vertex shader entry point of the [=pipeline=] and its input buffer layouts.

    : <dfn>primitive</dfn>
    ::
        Describes the primitive-related properties of the [=pipeline=].

    : <dfn>depthStencil</dfn>
    ::
        Describes the optional depth-stencil properties, including the testing, operations, and bias.

    : <dfn>multisample</dfn>
    ::
        Describes the multi-sampling properties of the [=pipeline=].

    : <dfn>fragment</dfn>
    ::
        Describes the fragment shader entry point of the [=pipeline=] and its output colors. If
        not [=map/exist|provided=], the [[#no-color-output]] mode is enabled.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createRenderPipeline(descriptor)</dfn>
    ::
        Creates a {{GPURenderPipeline}} using [=immediate pipeline creation=].

        <div algorithm=GPUDevice.createRenderPipeline>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createRenderPipeline(descriptor)">
                    |descriptor|: Description of the {{GPURenderPipeline}} to create.
                </pre>

                **Returns:** {{GPURenderPipeline}}

                [=Content timeline=] steps:

                1. If |descriptor|.{{GPURenderPipelineDescriptor/fragment}} is [=map/exist|provided=]:
                    1. [=list/For each=] non-`null` |colorState| of
                        |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}:
                        1. [=?=] [$Validate texture format required features$] of
                            |colorState|.{{GPUColorTargetState/format}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. If |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}} is [=map/exist|provided=]:
                    1. [=?=] [$Validate texture format required features$] of
                        |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.{{GPUDepthStencilState/format}}
                        with |this|.{{GPUObjectBase/[[device]]}}.
                1. Let |pipeline| be [=!=] [$create a new WebGPU object$](|this|, {{GPURenderPipeline}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |pipeline|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |layout| be a new [$default pipeline layout$] for |pipeline| if
                    |descriptor|.{{GPUPipelineDescriptorBase/layout}} is {{GPUAutoLayoutMode/"auto"}},
                    and |descriptor|.{{GPUPipelineDescriptorBase/layout}} otherwise.
                1. All of the requirements in the following steps |must| be met.
                    If any are unmet, [$generate a validation error$], [$invalidate$] |pipeline|, and return.

                    <div class=validusage>
                        1. |layout| |must| be [$valid to use with$] |this|.
                        1. [$validating GPURenderPipelineDescriptor$](|descriptor|, |layout|, |this|) must succeed.
                        1. Let |vertexBufferCount| be the index of the last non-null entry in
                            |descriptor|.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}},
                            plus 1; or 0 if there are none.
                        1. |layout|.{{GPUPipelineLayout/[[bindGroupLayouts]]}}.[=list/size=] + |vertexBufferCount| must be &le;
                            |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxBindGroupsPlusVertexBuffers}}.
                    </div>
                1. If any [=pipeline-creation error|pipeline-creation=] [=uncategorized errors=]
                    result from the implementation of pipeline creation,
                    [$generate an internal error$], [$invalidate$] |pipeline| and return.

                    Note:
                    Even if the implementation detected [=uncategorized errors=] in shader module
                    creation, the error is surfaced here.
                1. Set |pipeline|.{{GPURenderPipeline/[[descriptor]]}} to |descriptor|.
                1. Set |pipeline|.{{GPURenderPipeline/[[writesDepth]]}} to false.
                1. Set |pipeline|.{{GPURenderPipeline/[[writesStencil]]}} to false.
                1. Let |depthStencil| be |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.
                1. If |depthStencil| is not null:
                    1. If |depthStencil|.{{GPUDepthStencilState/depthWriteEnabled}} is [=map/exist|provided=]:
                        1. Set |pipeline|.{{GPURenderPipeline/[[writesDepth]]}} to |depthStencil|.{{GPUDepthStencilState/depthWriteEnabled}}.
                    1. If |depthStencil|.{{GPUDepthStencilState/stencilWriteMask}} is not 0:
                        1. Let |stencilFront| be |depthStencil|.{{GPUDepthStencilState/stencilFront}}.
                        1. Let |stencilBack| be |depthStencil|.{{GPUDepthStencilState/stencilBack}}.
                        1. Let |cullMode| be |descriptor|.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/cullMode}}.
                        1. If |cullMode| is not {{GPUCullMode/"front"}}, and any of |stencilFront|.{{GPUStencilFaceState/passOp}},
                            |stencilFront|.{{GPUStencilFaceState/depthFailOp}}, or |stencilFront|.{{GPUStencilFaceState/failOp}}
                            is not {{GPUStencilOperation/"keep"}}:
                            1. Set |pipeline|.{{GPURenderPipeline/[[writesStencil]]}} to true.
                        1. If |cullMode| is not {{GPUCullMode/"back"}}, and any of |stencilBack|.{{GPUStencilFaceState/passOp}},
                            |stencilBack|.{{GPUStencilFaceState/depthFailOp}}, or |stencilBack|.{{GPUStencilFaceState/failOp}}
                            is not {{GPUStencilOperation/"keep"}}:
                            1. Set |pipeline|.{{GPURenderPipeline/[[writesStencil]]}} to true.
                1. Set |pipeline|.{{GPUPipelineBase/[[layout]]}} to |layout|.
            </div>
        </div>

    : <dfn>createRenderPipelineAsync(descriptor)</dfn>
    ::
        Creates a {{GPURenderPipeline}} using [=async pipeline creation=].
        The returned {{Promise}} resolves when the created pipeline
        is ready to be used without additional delay.

        If pipeline creation fails, the returned {{Promise}} rejects with an {{GPUPipelineError}}.
        (A {{GPUError}} is not dispatched to the device.)

        Note: Use of this method is preferred whenever possible, as it prevents blocking the
        [=queue timeline=] work on pipeline compilation.

        <div algorithm=GPUDevice.createRenderPipelineAsync>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createRenderPipelineAsync(descriptor)">
                    |descriptor|: Description of the {{GPURenderPipeline}} to create.
                </pre>

                **Returns:** {{Promise}}&lt;{{GPURenderPipeline}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. Let |pipeline| be a new {{GPURenderPipeline}} created as if
                    |this|.{{GPUDevice/createRenderPipeline()}} was called with |descriptor|,
                    except capturing any errors as |error|, rather than dispatching them to the device.
                1. Let |event| occur upon the (successful or unsuccessful) completion of
                    [=pipeline creation=] for |pipeline|.
                1. [$Listen for timeline event$] |event|
                    on |this|.{{GPUObjectBase/[[device]]}}, handled by
                    the subsequent steps on the [=device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If |pipeline| is [$valid$] or |this| is [$invalid|lost$]:

                    1. Issue the following steps on |contentTimeline|:

                        <div data-timeline=content>
                            [=Content timeline=] steps:

                            1. [=Resolve=] |promise| with |pipeline|.
                        </div>

                    1. Return.

                    Note: No errors are generated from a device which is lost.
                    See [[#errors-and-debugging]].

                1. If |pipeline| is [$invalid$] and |error| is an [$internal error$],
                    issue the following steps on |contentTimeline|, and return.

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with a {{GPUPipelineError}} with
                            {{GPUPipelineErrorInit/reason}} {{GPUPipelineErrorReason/"internal"}}.
                    </div>

                1. If |pipeline| is [$invalid$] and |error| is a [$validation error$],
                    issue the following steps on |contentTimeline|, and return.

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with a {{GPUPipelineError}} with
                            {{GPUPipelineErrorInit/reason}} {{GPUPipelineErrorReason/"validation"}}.
                    </div>
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPURenderPipelineDescriptor</dfn>(descriptor, layout, device)

    **Arguments:**

    - {{GPURenderPipelineDescriptor}} |descriptor|
    - {{GPUPipelineLayout}} |layout|
    - {{GPUDevice}} |device|

    [=Device timeline=] steps:

    1. Return `true` if all of the following conditions are satisfied:

        <div class=validusage>
            - [$validating GPUVertexState$](|device|, |descriptor|.{{GPURenderPipelineDescriptor/vertex}}, |layout|) succeeds.
            - If |descriptor|.{{GPURenderPipelineDescriptor/fragment}} is [=map/exist|provided=]:
                - [$validating GPUFragmentState$](|device|, |descriptor|.{{GPURenderPipelineDescriptor/fragment}}, |layout|) succeeds.
                - If the [=builtin/sample_mask=] builtin is a [=shader stage output=] of
                    |descriptor|.{{GPURenderPipelineDescriptor/fragment}}:
                    - |descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/alphaToCoverageEnabled}} is `false`.
                - If the [=builtin/frag_depth=] builtin is a [=shader stage output=] of
                    |descriptor|.{{GPURenderPipelineDescriptor/fragment}}:
                    - |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}} must be
                        [=map/exist|provided=], and
                        |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.{{GPUDepthStencilState/format}}
                        must have a [=aspect/depth=] aspect.
            - [$validating GPUPrimitiveState$](|descriptor|.{{GPURenderPipelineDescriptor/primitive}}, |device|) succeeds.
            - If |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}} is [=map/exist|provided=]:
                - [$validating GPUDepthStencilState$](|descriptor|.{{GPURenderPipelineDescriptor/depthStencil}},
                    |descriptor|.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/topology}}) succeeds.
            - [$validating GPUMultisampleState$](|descriptor|.{{GPURenderPipelineDescriptor/multisample}}) succeeds.
            - If |descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/alphaToCoverageEnabled}}
                is true:
                1. |descriptor|.{{GPURenderPipelineDescriptor/fragment}} must be [=map/exist|provided=].
                1. |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}[0]
                    must [=list/exist=] and be non-null.
                1. |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}[0].{{GPUColorTargetState/format}}
                    must be a {{GPUTextureFormat}} which is [=blendable=] and has an alpha channel.
            - There must exist at least one attachment, either:
                - A non-`null` value in
                    |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}, or
                - A |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.
            - [$validating inter-stage interfaces$](|device|, |descriptor|) returns `true`.
        </div>
</div>

<div algorithm class=validusage data-timeline=device>
    <dfn abstract-op>validating inter-stage interfaces</dfn>(|device|, |descriptor|)

    **Arguments:**

    - {{GPUDevice}} |device|
    - {{GPURenderPipelineDescriptor}} |descriptor|

    **Returns:** {{boolean}}

    [=Device timeline=] steps:

    1. Let |maxVertexShaderOutputVariables| be
        |device|.limits.{{supported limits/maxInterStageShaderVariables}}.
    1. Let |maxVertexShaderOutputLocation| be
        |device|.limits.{{supported limits/maxInterStageShaderVariables}} - 1.
    1. If |descriptor|.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/topology}}
        is {{GPUPrimitiveTopology/"point-list"}}:
        1. Decrement |maxVertexShaderOutputVariables| by 1.
    1. If [=builtin/clip_distances=] is declared in the output of
        |descriptor|.{{GPURenderPipelineDescriptor/vertex}}:
        1. Let |clipDistancesSize| be the array size of [=builtin/clip_distances=].
        1. Decrement |maxVertexShaderOutputVariables| by ceil(|clipDistancesSize| / 4).
        1. Decrement |maxVertexShaderOutputLocation| by ceil(|clipDistancesSize| / 4).
    1. Return `false` if any of the following requirements are unmet:
        - There must be no more than |maxVertexShaderOutputVariables| user-defined outputs for
            |descriptor|.{{GPURenderPipelineDescriptor/vertex}}.
        - The [=location=] of each user-defined output of
            |descriptor|.{{GPURenderPipelineDescriptor/vertex}} must be
            &le; |maxVertexShaderOutputLocation|.
    1. If |descriptor|.{{GPURenderPipelineDescriptor/fragment}} [=map/exist|is provided=]:
        1. Let |maxFragmentShaderInputVariables| be
            |device|.limits.{{supported limits/maxInterStageShaderVariables}}.
        1. If any of the `front_facing`, `sample_index`, or `sample_mask` [=builtins=] are an input of
            |descriptor|.{{GPURenderPipelineDescriptor/fragment}}:
            1. Decrement |maxFragmentShaderInputVariables| by 1.
        1. Return `false` if any of the following requirements are unmet:
            - For each user-defined input of |descriptor|.{{GPURenderPipelineDescriptor/fragment}} there
                must be a user-defined output of |descriptor|.{{GPURenderPipelineDescriptor/vertex}} that
                [=location=], type, and [=interpolation=] of the input.

                Note: Vertex-only pipelines **can** have user-defined outputs in the vertex stage;
                their values will be discarded.
            - There must be no more than |maxFragmentShaderInputVariables| user-defined inputs for
                |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.
        1. [=Assert=] that the [=location=] of each user-defined input of
            |descriptor|.{{GPURenderPipelineDescriptor/fragment}} is less
            than |device|.limits.{{supported limits/maxInterStageShaderVariables}}.
            (This follows from the above rules.)
    1. Return `true`.
</div>

<div class=example>
    Creating a simple {{GPURenderPipeline}}:

    <pre highlight=js>
        const renderPipeline = gpuDevice.createRenderPipeline({
            layout: pipelineLayout,
            vertex: {
                module: shaderModule,
                entryPoint: 'vertexMain'
            },
            fragment: {
                module: shaderModule,
                entryPoint: 'fragmentMain',
                targets: [{
                    format: 'bgra8unorm',
                }],
            }
        });
    </pre>
</div>

### Primitive State ### {#primitive-state}

<script type=idl>
dictionary GPUPrimitiveState {
    GPUPrimitiveTopology topology = "triangle-list";
    GPUIndexFormat stripIndexFormat;
    GPUFrontFace frontFace = "ccw";
    GPUCullMode cullMode = "none";

    // Requires "depth-clip-control" feature.
    boolean unclippedDepth = false;
};
</script>

{{GPUPrimitiveState}} has the following members, which describe how a {{GPURenderPipeline}}
constructs and rasterizes primitives from its vertex inputs:

<dl dfn-type=dict-member dfn-for=GPUPrimitiveState>
    : <dfn>topology</dfn>
    ::
        The type of primitive to be constructed from the vertex inputs.

    : <dfn>stripIndexFormat</dfn>
    ::
        For pipelines with strip topologies
        ({{GPUPrimitiveTopology/"line-strip"}} or {{GPUPrimitiveTopology/"triangle-strip"}}),
        this determines the index buffer format and primitive restart value
        ({{GPUIndexFormat/"uint16"}}/`0xFFFF` or {{GPUIndexFormat/"uint32"}}/`0xFFFFFFFF`).
        It is not allowed on pipelines with non-strip topologies.

        Note: Some implementations require knowledge of the primitive restart value to compile
        pipeline state objects.

        To use a strip-topology pipeline with an indexed draw call
        ({{GPURenderCommandsMixin/drawIndexed()}} or {{GPURenderCommandsMixin/drawIndexedIndirect()}}),
        this must be set, and it must match the index buffer format used with the draw call
        (set in {{GPURenderCommandsMixin/setIndexBuffer()}}).

        See [[#primitive-assembly]] for additional details.

    : <dfn>frontFace</dfn>
    ::
        Defines which polygons are considered [=front-facing=].

    : <dfn>cullMode</dfn>
    ::
        Defines which polygon orientation will be culled, if any.

    : <dfn>unclippedDepth</dfn>
    ::
        If true, indicates that [=depth clipping=] is disabled.

        Requires the {{GPUFeatureName/"depth-clip-control"}} feature to be enabled.
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUPrimitiveState</dfn>(|descriptor|, |device|)
        **Arguments:**

        - {{GPUPrimitiveState}} |descriptor|
        - {{GPUDevice}} |device|

        [=Device timeline=] steps:

        1. Return `true` if all of the following conditions are satisfied:

            <div class=validusage>
                - If |descriptor|.{{GPUPrimitiveState/topology}} is not
                    {{GPUPrimitiveTopology/"line-strip"}} or {{GPUPrimitiveTopology/"triangle-strip"}}:
                    - |descriptor|.{{GPUPrimitiveState/stripIndexFormat}} must not be [=map/exist|provided=].
                - If |descriptor|.{{GPUPrimitiveState/unclippedDepth}} is `true`:
                    - {{GPUFeatureName/"depth-clip-control"}} must be [=enabled for=] |device|.
            </div>
</div>

<script type=idl>
enum GPUPrimitiveTopology {
    "point-list",
    "line-list",
    "line-strip",
    "triangle-list",
    "triangle-strip",
};
</script>

{{GPUPrimitiveTopology}} defines the primitive type draw calls made with a {{GPURenderPipeline}}
will use. See [[#rasterization]] for additional details:

<dl dfn-type=enum-value dfn-for=GPUPrimitiveTopology>
    : <dfn>"point-list"</dfn>
    ::
        Each vertex defines a point primitive.

    : <dfn>"line-list"</dfn>
    ::
        Each consecutive pair of two vertices defines a line primitive.

    : <dfn>"line-strip"</dfn>
    ::
        Each vertex after the first defines a line primitive between it and the previous vertex.

    : <dfn>"triangle-list"</dfn>
    ::
        Each consecutive triplet of three vertices defines a triangle primitive.

    : <dfn>"triangle-strip"</dfn>
    ::
        Each vertex after the first two defines a triangle primitive between it and the previous
        two vertices.
</dl>

<script type=idl>
enum GPUFrontFace {
    "ccw",
    "cw",
};
</script>

{{GPUFrontFace}} defines which polygons are considered [=front-facing=] by a {{GPURenderPipeline}}.
See [[#polygon-rasterization]] for additional details:

<dl dfn-type=enum-value dfn-for=GPUFrontFace>
    : <dfn>"ccw"</dfn>
    ::
        Polygons with vertices whose framebuffer coordinates are given in counter-clockwise order
        are considered [=front-facing=].

    : <dfn>"cw"</dfn>
    ::
        Polygons with vertices whose framebuffer coordinates are given in clockwise order are
        considered [=front-facing=].
</dl>

<script type=idl>
enum GPUCullMode {
    "none",
    "front",
    "back",
};
</script>

{{GPUPrimitiveTopology}} defines which polygons will be culled by draw calls made with a
{{GPURenderPipeline}}. See [[#polygon-rasterization]] for additional details:

<dl dfn-type=enum-value dfn-for=GPUCullMode>
    : <dfn>"none"</dfn>
    ::
        No polygons are discarded.

    : <dfn>"front"</dfn>
    ::
        [=Front-facing=] polygons are discarded.

    : <dfn>"back"</dfn>
    ::
        [=Back-facing=] polygons are discarded.
</dl>

Note: {{GPUFrontFace}} and {{GPUCullMode}} have no effect on {{GPUPrimitiveTopology/"point-list"}},
{{GPUPrimitiveTopology/"line-list"}}, or {{GPUPrimitiveTopology/"line-strip"}} topologies.

### Multisample State ### {#multisample-state}

<script type=idl>
dictionary GPUMultisampleState {
    GPUSize32 count = 1;
    GPUSampleMask mask = 0xFFFFFFFF;
    boolean alphaToCoverageEnabled = false;
};
</script>

{{GPUMultisampleState}} has the following members, which describe how a {{GPURenderPipeline}}
interacts with a render pass's multisampled attachments.

<dl dfn-type=dict-member dfn-for=GPUMultisampleState>
    : <dfn>count</dfn>
    ::
        Number of samples per pixel. This {{GPURenderPipeline}} will be compatible only
        with attachment textures ({{GPURenderPassDescriptor/colorAttachments}}
        and {{GPURenderPassDescriptor/depthStencilAttachment}})
        with matching {{GPUTextureDescriptor/sampleCount}}s.

    : <dfn>mask</dfn>
    ::
        Mask determining which samples are written to.

    : <dfn>alphaToCoverageEnabled</dfn>
    ::
        When `true` indicates that a fragment's alpha channel should be used to generate a sample
        coverage mask.
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUMultisampleState</dfn>(|descriptor|)
        **Arguments:**

        - {{GPUMultisampleState}} |descriptor|

        [=Device timeline=] steps:

        1. Return `true` if all of the following conditions are satisfied:

            <div class=validusage>
                - |descriptor|.{{GPUMultisampleState/count}} must be either 1 or 4.
                - If |descriptor|.{{GPUMultisampleState/alphaToCoverageEnabled}} is `true`:
                    - |descriptor|.{{GPUMultisampleState/count}} &gt; 1.
            </div>
</div>

### Fragment State ### {#fragment-state}

<script type=idl>
dictionary GPUFragmentState
         : GPUProgrammableStage {
    required sequence<GPUColorTargetState?> targets;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUFragmentState>
    : <dfn>targets</dfn>
    ::
        A list of {{GPUColorTargetState}} defining the formats and behaviors of the color targets
        this pipeline writes to.
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUFragmentState</dfn>(|device|, |descriptor|, |layout|)

    **Arguments:**

    - {{GPUDevice}} |device|
    - {{GPUFragmentState}} |descriptor|
    - {{GPUPipelineLayout}} |layout|

    [=Device timeline=] steps:

    1. Return `true` if all of the following requirements are met:

        <div class=validusage>
            - [$validating GPUProgrammableStage$]({{GPUShaderStage/FRAGMENT}}, |descriptor|, |layout|, |device|) succeeds.
            - |descriptor|.{{GPUFragmentState/targets}}.[=list/size=] must be &le;
                |device|.{{device/[[limits]]}}.{{supported limits/maxColorAttachments}}.
            - Let |entryPoint| be [$get the entry point$]({{GPUShaderStage/FRAGMENT}}, |descriptor|).
            - Let |usesDualSourceBlending| be `false`.
            - [=list/For each=] |index| of the [=list/indices=] of |descriptor|.{{GPUFragmentState/targets}}
                containing a non-`null` value |colorState|:
                - |colorState|.{{GPUColorTargetState/format}} must be listed in [[#plain-color-formats]]
                    with {{GPUTextureUsage/RENDER_ATTACHMENT}} capability.
                - |colorState|.{{GPUColorTargetState/writeMask}} must be &lt; 16.
                - If |colorState|.{{GPUColorTargetState/blend}} is [=map/exist|provided=]:
                    - The |colorState|.{{GPUColorTargetState/format}} must be [=blendable=].
                    - |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/color}}
                        must be a [=valid GPUBlendComponent=].
                    - |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/alpha}}
                        must be a [=valid GPUBlendComponent=].
                    - If |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/color}}.{{GPUBlendComponent/srcFactor}} or
                        |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/color}}.{{GPUBlendComponent/dstFactor}} or
                        |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/alpha}}.{{GPUBlendComponent/srcFactor}} or
                        |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/alpha}}.{{GPUBlendComponent/dstFactor}}
                        uses the second input of the corresponding blending unit (is any of
                            {{GPUBlendFactor/"src1"}}, {{GPUBlendFactor/"one-minus-src1"}},
                            {{GPUBlendFactor/"src1-alpha"}}, {{GPUBlendFactor/"one-minus-src1-alpha"}}), then:

                        - Set |usesDualSourceBlending| to `true`.
                - For each [=shader stage output=] value |output| with [=location=] attribute equal to |index|
                    in |entryPoint|:
                    - For each component in |colorState|.{{GPUColorTargetState/format}}, there must be a
                        corresponding component in |output|.
                        (That is, RGBA requires vec4, RGB requires vec3 or vec4, RG requires vec2 or vec3 or vec4.)
                    - If the {{GPUTextureSampleType}}s for |colorState|.{{GPUColorTargetState/format}}
                        (defined in [[#texture-format-caps]]) are:

                        <dl class=switch>
                            : {{GPUTextureSampleType/"float"}} and/or {{GPUTextureSampleType/"unfilterable-float"}}
                            :: |output| must have a floating-point scalar type.
                            : {{GPUTextureSampleType/"sint"}}
                            :: |output| must have a signed integer scalar type.
                            : {{GPUTextureSampleType/"uint"}}
                            :: |output| must have an unsigned integer scalar type.
                        </dl>
                    - If |colorState|.{{GPUColorTargetState/blend}} is [=map/exist|provided=] and
                        |colorState|.{{GPUColorTargetState/blend}}.{{GPUBlendState/color}}.{{GPUBlendComponent/srcFactor}}
                        or .{{GPUBlendComponent/dstFactor}} uses the source alpha
                        (is any of {{GPUBlendFactor/"src-alpha"}}, {{GPUBlendFactor/"one-minus-src-alpha"}},
                        {{GPUBlendFactor/"src-alpha-saturated"}}, {{GPUBlendFactor/"src1-alpha"}} or
                        {{GPUBlendFactor/"one-minus-src1-alpha"}}), then:
                        - |output| must have an alpha channel (that is, it must be a vec4).

                - If |colorState|.{{GPUColorTargetState/writeMask}} is not 0:
                    - |entryPoint| must have a [=shader stage output=] with [=location=] equal to |index|
                         and [=blend_src=] omitted or equal to 0.
            - If |usesDualSourceBlending| is `true`:
                - |descriptor|.{{GPUFragmentState/targets}}.[=list/size=] must be 1.
                - All the [=shader stage outputs=] with [=location=] in |entryPoint| must be in one
                    struct and [=use dual source blending=].
            - [$Validating GPUFragmentState's color attachment bytes per sample$](|device|, |descriptor|.{{GPUFragmentState/targets}}) succeeds.
        </div>
</div>

<div algorithm class=validusage data-timeline=device>
    <dfn abstract-op>Validating GPUFragmentState's color attachment bytes per sample</dfn>(|device|, |targets|)

    **Arguments:**

    - {{GPUDevice}} |device|
    - [=sequence=]&lt;{{GPUColorTargetState}}?&gt; |targets|

    [=Device timeline=] steps:

    1. Let |formats| be an empty [=list=]&lt;{{GPUTextureFormat}}?&gt;
    1. For each |target| in |targets|:
        1. If |target| is `undefined`, continue.
        1. [=list/Append=] |target|.{{GPUColorTargetState/format}} to |formats|.
    1. [$Calculating color attachment bytes per sample$](|formats|) must be &le; |device|.{{device/[[limits]]}}.{{supported limits/maxColorAttachmentBytesPerSample}}.
</div>

Note:
The fragment shader may output more values than what the pipeline uses. If that is the case
the values are ignored.

<div algorithm data-timeline=device>
    {{GPUBlendComponent}} |component| is a <dfn>valid GPUBlendComponent</dfn> with logical
    [=device=] |device| if it meets<br/>
    the following requirements:

    <div class=validusage>
        - If |component|.{{GPUBlendComponent/operation}} is
            {{GPUBlendOperation/"min"}} or {{GPUBlendOperation/"max"}}:
            - |component|.{{GPUBlendComponent/srcFactor}} and
                |component|.{{GPUBlendComponent/dstFactor}} must both be {{GPUBlendFactor/"one"}}.
        - If |component|.{{GPUBlendComponent/srcFactor}} or
            |component|.{{GPUBlendComponent/dstFactor}} requires a feature according to the
            {{GPUBlendFactor}} table and |device|.{{device/[[features]]}} does not [=list/contain=]
            the feature:
            - Throw a {{TypeError}}.
    </div>
</div>

### Color Target State ### {#color-target-state}

<script type=idl>
dictionary GPUColorTargetState {
    required GPUTextureFormat format;

    GPUBlendState blend;
    GPUColorWriteFlags writeMask = 0xF;  // GPUColorWrite.ALL
};
</script>

<dl dfn-type=dict-member dfn-for=GPUColorTargetState>
    : <dfn>format</dfn>
    ::
        The {{GPUTextureFormat}} of this color target. The pipeline will only be compatible with
        {{GPURenderPassEncoder}}s which use a {{GPUTextureView}} of this format in the
        corresponding color attachment.

    : <dfn>blend</dfn>
    ::
        The blending behavior for this color target. If left undefined, disables blending for this
        color target.

    : <dfn>writeMask</dfn>
    ::
        Bitmask controlling which channels are are written to when drawing to this color target.
</dl>

<script type=idl>
dictionary GPUBlendState {
    required GPUBlendComponent color;
    required GPUBlendComponent alpha;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUBlendState>
    : <dfn>color</dfn>
    ::
        Defines the blending behavior of the corresponding render target for color channels.

    : <dfn>alpha</dfn>
    ::
        Defines the blending behavior of the corresponding render target for the alpha channel.
</dl>

<script type=idl>
typedef [EnforceRange] unsigned long GPUColorWriteFlags;
[Exposed=(Window, Worker), SecureContext]
namespace GPUColorWrite {
    const GPUFlagsConstant RED   = 0x1;
    const GPUFlagsConstant GREEN = 0x2;
    const GPUFlagsConstant BLUE  = 0x4;
    const GPUFlagsConstant ALPHA = 0x8;
    const GPUFlagsConstant ALL   = 0xF;
};
</script>

#### Blend State #### {#blend-state}

<script type=idl>
dictionary GPUBlendComponent {
    GPUBlendOperation operation = "add";
    GPUBlendFactor srcFactor = "one";
    GPUBlendFactor dstFactor = "zero";
};
</script>

{{GPUBlendComponent}} has the following members, which describe how the color or alpha components
of a fragment are blended:

<dl dfn-type=dict-member dfn-for=GPUBlendComponent>
    : <dfn>operation</dfn>
    ::
        Defines the {{GPUBlendOperation}} used to calculate the values written to the target
        attachment components.

    : <dfn>srcFactor</dfn>
    ::
        Defines the {{GPUBlendFactor}} operation to be performed on values from the fragment shader.

    : <dfn>dstFactor</dfn>
    ::
        Defines the {{GPUBlendFactor}} operation to be performed on values from the target attachment.
</dl>

The following tables use this notation to describe color components for a given fragment
location:
<table class=data>
    <tbody>
        <tr>
            <td><b><code>RGBA<sub>src</sub></code></b>
            <td>Color output by the fragment shader for the color attachment.
                If the shader doesn't return an alpha channel, src-alpha blend factors cannot be used.
        <tr>
            <td><b><code>RGBA<sub>src1</sub></code></b>
            <td>Color output by the fragment shader for the color attachment with
                <a href="https://gpuweb.github.io/gpuweb/wgsl/#input-output-locations">"@blend_src" attribute</a>
                equal to `1`.
                If the shader doesn't return an alpha channel, src1-alpha blend factors cannot be used.
        <tr>
            <td><b><code>RGBA<sub>dst</sub></code></b>
            <td>Color currently in the color attachment.
                Missing green/blue/alpha channels default to `0, 0, 1`, respectively.
        <tr>
            <td><b><code>RGBA<sub>const</sub></code></b>
            <td>The current {{RenderState/[[blendConstant]]}}.
        <tr>
            <td><b><code>RGBA<sub>srcFactor</sub></code></b>
            <td>The source blend factor components, as defined by {{GPUBlendComponent/srcFactor}}.
        <tr>
            <td><b><code>RGBA<sub>dstFactor</sub></code></b>
            <td>The destination blend factor components, as defined by {{GPUBlendComponent/dstFactor}}.
    </tbody>
</table>

<script type=idl>
enum GPUBlendFactor {
    "zero",
    "one",
    "src",
    "one-minus-src",
    "src-alpha",
    "one-minus-src-alpha",
    "dst",
    "one-minus-dst",
    "dst-alpha",
    "one-minus-dst-alpha",
    "src-alpha-saturated",
    "constant",
    "one-minus-constant",
    "src1",
    "one-minus-src1",
    "src1-alpha",
    "one-minus-src1-alpha",
};
</script>

{{GPUBlendFactor}} defines how either a source or destination blend factors is calculated:

<table class=data>
    <thead>
        <tr>
            <th>GPUBlendFactor
            <th>Blend factor RGBA components
            <th>[=Feature=]
        </tr>
    </thead>
    <tbody dfn-type=enum-value dfn-for=GPUBlendFactor>
        <tr>
            <td><dfn>"zero"</dfn>
            <td><code>(0, 0, 0, 0)</code>
            <td rowspan=13>
        <tr>
            <td><dfn>"one"</dfn>
            <td><code>(1, 1, 1, 1)</code>
        <tr>
            <td><dfn>"src"</dfn>
            <td><code>(R<sub>src</sub>, G<sub>src</sub>, B<sub>src</sub>, A<sub>src</sub>)</code>
        <tr>
            <td><dfn>"one-minus-src"</dfn>
            <td><code>(1 - R<sub>src</sub>, 1 - G<sub>src</sub>, 1 - B<sub>src</sub>, 1 - A<sub>src</sub>)</code>
        <tr>
            <td><dfn>"src-alpha"</dfn>
            <td><code>(A<sub>src</sub>, A<sub>src</sub>, A<sub>src</sub>, A<sub>src</sub>)</code>
        <tr>
            <td><dfn>"one-minus-src-alpha"</dfn>
            <td><code>(1 - A<sub>src</sub>, 1 - A<sub>src</sub>, 1 - A<sub>src</sub>, 1 - A<sub>src</sub>)</code>
        <tr>
            <td><dfn>"dst"</dfn>
            <td><code>(R<sub>dst</sub>, G<sub>dst</sub>, B<sub>dst</sub>, A<sub>dst</sub>)</code>
        <tr>
            <td><dfn>"one-minus-dst"</dfn>
            <td><code>(1 - R<sub>dst</sub>, 1 - G<sub>dst</sub>, 1 - B<sub>dst</sub>, 1 - A<sub>dst</sub>)</code>
        <tr>
            <td><dfn>"dst-alpha"</dfn>
            <td><code>(A<sub>dst</sub>, A<sub>dst</sub>, A<sub>dst</sub>, A<sub>dst</sub>)</code>
        <tr>
            <td><dfn>"one-minus-dst-alpha"</dfn>
            <td><code>(1 - A<sub>dst</sub>, 1 - A<sub>dst</sub>, 1 - A<sub>dst</sub>, 1 - A<sub>dst</sub>)</code>
        <tr>
            <td><dfn>"src-alpha-saturated"</dfn>
            <td><code>(min(A<sub>src</sub>, 1 - A<sub>dst</sub>), min(A<sub>src</sub>, 1 - A<sub>dst</sub>), min(A<sub>src</sub>, 1 - A<sub>dst</sub>), 1)</code>
        <tr>
            <td><dfn>"constant"</dfn>
            <td><code>(R<sub>const</sub>, G<sub>const</sub>, B<sub>const</sub>, A<sub>const</sub>)</code>
        <tr>
            <td><dfn>"one-minus-constant"</dfn>
            <td><code>(1 - R<sub>const</sub>, 1 - G<sub>const</sub>, 1 - B<sub>const</sub>, 1 - A<sub>const</sub>)</code>
        <tr>
            <td><dfn>"src1"</dfn>
            <td><code>(R<sub>src1</sub>, G<sub>src1</sub>, B<sub>src1</sub>, A<sub>src1</sub>)</code>
            <td rowspan=4>{{GPUFeatureName/dual-source-blending}}
        <tr>
            <td><dfn>"one-minus-src1"</dfn>
            <td><code>(1 - R<sub>src1</sub>, 1 - G<sub>src1</sub>, 1 - B<sub>src1</sub>, 1 - A<sub>src1</sub>)</code>
        <tr>
            <td><dfn>"src1-alpha"</dfn>
            <td><code>(A<sub>src1</sub>, A<sub>src1</sub>, A<sub>src1</sub>, A<sub>src1</sub>)</code>
        <tr>
            <td><dfn>"one-minus-src1-alpha"</dfn>
            <td><code>(1 - A<sub>src1</sub>, 1 - A<sub>src1</sub>, 1 - A<sub>src1</sub>, 1 - A<sub>src1</sub>)</code>
    </tbody>
</table>

<script type=idl>
enum GPUBlendOperation {
    "add",
    "subtract",
    "reverse-subtract",
    "min",
    "max",
};
</script>

{{GPUBlendOperation}} defines the algorithm used to combine source and destination blend factors:

<table class=data>
    <thead>
        <tr>
            <th>GPUBlendOperation
            <th>RGBA Components
        </tr>
    </thead>
    <tbody dfn-type=enum-value dfn-for=GPUBlendOperation>
        <tr>
            <td><dfn>"add"</dfn>
            <td><code>RGBA<sub>src</sub> &times; RGBA<sub>srcFactor</sub> + RGBA<sub>dst</sub> &times; RGBA<sub>dstFactor</sub></code>
        <tr>
            <td><dfn>"subtract"</dfn>
            <td><code>RGBA<sub>src</sub> &times; RGBA<sub>srcFactor</sub> - RGBA<sub>dst</sub> &times; RGBA<sub>dstFactor</sub></code>
        <tr>
            <td><dfn>"reverse-subtract"</dfn>
            <td><code>RGBA<sub>dst</sub> &times; RGBA<sub>dstFactor</sub> - RGBA<sub>src</sub> &times; RGBA<sub>srcFactor</sub></code>
        <tr>
            <td><dfn>"min"</dfn>
            <td><code>min(RGBA<sub>src</sub>, RGBA<sub>dst</sub>)</code>
        <tr>
            <td><dfn>"max"</dfn>
            <td><code>max(RGBA<sub>src</sub>, RGBA<sub>dst</sub>)</code>
    </tbody>
</table>

### Depth/Stencil State ### {#depth-stencil-state}

<script type=idl>
dictionary GPUDepthStencilState {
    required GPUTextureFormat format;

    boolean depthWriteEnabled;
    GPUCompareFunction depthCompare;

    GPUStencilFaceState stencilFront = {};
    GPUStencilFaceState stencilBack = {};

    GPUStencilValue stencilReadMask = 0xFFFFFFFF;
    GPUStencilValue stencilWriteMask = 0xFFFFFFFF;

    GPUDepthBias depthBias = 0;
    float depthBiasSlopeScale = 0;
    float depthBiasClamp = 0;
};
</script>

{{GPUDepthStencilState}} has the following members, which describe how a {{GPURenderPipeline}}
will affect a render pass's {{GPURenderPassDescriptor/depthStencilAttachment}}:

<dl dfn-type=dict-member dfn-for=GPUDepthStencilState>
    : <dfn>format</dfn>
    ::
        The {{GPUTextureViewDescriptor/format}} of {{GPURenderPassDescriptor/depthStencilAttachment}}
        this {{GPURenderPipeline}} will be compatible with.

    : <dfn>depthWriteEnabled</dfn>
    ::
        Indicates if this {{GPURenderPipeline}} can modify
        {{GPURenderPassDescriptor/depthStencilAttachment}} depth values.

    : <dfn>depthCompare</dfn>
    ::
        The comparison operation used to test fragment depths against
        {{GPURenderPassDescriptor/depthStencilAttachment}} depth values.

    : <dfn>stencilFront</dfn>
    ::
        Defines how stencil comparisons and operations are performed for front-facing primitives.

    : <dfn>stencilBack</dfn>
    ::
        Defines how stencil comparisons and operations are performed for back-facing primitives.

    : <dfn>stencilReadMask</dfn>
    ::
        Bitmask controlling which {{GPURenderPassDescriptor/depthStencilAttachment}} stencil value
        bits are read when performing stencil comparison tests.

    : <dfn>stencilWriteMask</dfn>
    ::
        Bitmask controlling which {{GPURenderPassDescriptor/depthStencilAttachment}} stencil value
        bits are written to when performing stencil operations.

    : <dfn>depthBias</dfn>
    ::
        Constant depth bias added to each triangle fragment. See [$biased fragment depth$] for details.

    : <dfn>depthBiasSlopeScale</dfn>
    ::
        Depth bias that scales with the triangle fragment’s slope. See [$biased fragment depth$] for details.

    : <dfn>depthBiasClamp</dfn>
    ::
        The maximum depth bias of a triangle fragment. See [$biased fragment depth$] for details.
</dl>

Note: {{GPUDepthStencilState/depthBias}}, {{GPUDepthStencilState/depthBiasSlopeScale}}, and
{{GPUDepthStencilState/depthBiasClamp}} have no effect on {{GPUPrimitiveTopology/"point-list"}},
{{GPUPrimitiveTopology/"line-list"}}, and {{GPUPrimitiveTopology/"line-strip"}} primitives, and
must be 0.

<div algorithm data-timeline=queue>
    The <dfn abstract-op>biased fragment depth</dfn> for a fragment being written to
    {{GPURenderPassDescriptor/depthStencilAttachment}} |attachment| when drawing using
    {{GPUDepthStencilState}} |state| is calculated by running the following [=queue timeline=] steps:

    1. Let |format| be |attachment|.{{GPURenderPassDepthStencilAttachment/view}}.{{GPUTextureViewDescriptor/format}}.
    1. Let |r| be the minimum positive representable value &gt; `0` in the |format| converted to a 32-bit float.
    1. Let |maxDepthSlope| be the maximum of the horizontal and vertical slopes of the fragment's depth value.
    1. If |format| is a **unorm** format:
        1. Let |bias| be <code>(float)|state|.{{GPUDepthStencilState/depthBias}} * |r| + |state|.{{GPUDepthStencilState/depthBiasSlopeScale}} * |maxDepthSlope|</code>.

        Otherwise, if |format| is a **float** format:

        1. Let |bias| be <code>(float)|state|.{{GPUDepthStencilState/depthBias}} * 2^(exp(max depth in primitive) - |r|) + |state|.{{GPUDepthStencilState/depthBiasSlopeScale}} * |maxDepthSlope|</code>.
    1. If |state|.{{GPUDepthStencilState/depthBiasClamp}} &gt; `0`:
        1. Set |bias| to <code>min(|state|.{{GPUDepthStencilState/depthBiasClamp}}, |bias|)</code>.

        Otherwise, if |state|.{{GPUDepthStencilState/depthBiasClamp}} &lt; `0`:

        1. Set |bias| to <code>max(|state|.{{GPUDepthStencilState/depthBiasClamp}}, |bias|)</code>.
    1. If |state|.{{GPUDepthStencilState/depthBias}} &ne; `0` or |state|.{{GPUDepthStencilState/depthBiasSlopeScale}} &ne; `0`:
        1. Set the fragment depth value to <code>fragment depth value + |bias|</code>
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUDepthStencilState</dfn>(|descriptor|, |topology|)

    **Arguments:**

    - {{GPUDepthStencilState}} |descriptor|
    - {{GPUPrimitiveTopology}} |topology|

    [=Device timeline=] steps:

    1. Return `true` if, and only if, all of the following conditions are satisfied:

        <div class=validusage>
            - |descriptor|.{{GPUDepthStencilState/format}} is a [=depth-or-stencil format=].
            - If |descriptor|.{{GPUDepthStencilState/depthWriteEnabled}} is `true` or
                |descriptor|.{{GPUDepthStencilState/depthCompare}} is [=map/exist|provided=] and not
                {{GPUCompareFunction/"always"}}:
                - |descriptor|.{{GPUDepthStencilState/format}} must have a depth component.
            - If |descriptor|.{{GPUDepthStencilState/stencilFront}} or
                    |descriptor|.{{GPUDepthStencilState/stencilBack}} are not the default values:
                    - |descriptor|.{{GPUDepthStencilState/format}} must have a stencil component.
            - If |descriptor|.{{GPUDepthStencilState/format}} has a depth component:
                - |descriptor|.{{GPUDepthStencilState/depthWriteEnabled}} must be [=map/exist|provided=].
                - |descriptor|.{{GPUDepthStencilState/depthCompare}} must be [=map/exist|provided=] if:
                    - |descriptor|.{{GPUDepthStencilState/depthWriteEnabled}} is `true`, or
                    - |descriptor|.{{GPUDepthStencilState/stencilFront}}.{{GPUStencilFaceState/depthFailOp}}
                        is not {{GPUStencilOperation/"keep"}}, or
                    - |descriptor|.{{GPUDepthStencilState/stencilBack}}.{{GPUStencilFaceState/depthFailOp}}
                        is not {{GPUStencilOperation/"keep"}}.
            - If |topology| is {{GPUPrimitiveTopology/"point-list"}}, {{GPUPrimitiveTopology/"line-list"}}, or
                {{GPUPrimitiveTopology/"line-strip"}}:
                - |descriptor|.{{GPUDepthStencilState/depthBias}} must be 0.
                - |descriptor|.{{GPUDepthStencilState/depthBiasSlopeScale}} must be 0.
                - |descriptor|.{{GPUDepthStencilState/depthBiasClamp}} must be 0.
        </div>

</div>

<script type=idl>
dictionary GPUStencilFaceState {
    GPUCompareFunction compare = "always";
    GPUStencilOperation failOp = "keep";
    GPUStencilOperation depthFailOp = "keep";
    GPUStencilOperation passOp = "keep";
};
</script>

{{GPUStencilFaceState}} has the following members, which describe how stencil comparisons and
operations are performed:

<dl dfn-type=dict-member dfn-for=GPUStencilFaceState>
    : <dfn>compare</dfn>
    ::
        The {{GPUCompareFunction}} used when testing the {{RenderState/[[stencilReference]]}} value
        against the fragment's {{GPURenderPassDescriptor/depthStencilAttachment}} stencil values.

    : <dfn>failOp</dfn>
    ::
        The {{GPUStencilOperation}} performed if the fragment stencil comparison test described by
        {{GPUStencilFaceState/compare}} fails.

    : <dfn>depthFailOp</dfn>
    ::
        The {{GPUStencilOperation}} performed if the fragment depth comparison described by
        {{GPUDepthStencilState/depthCompare}} fails.

    : <dfn>passOp</dfn>
    ::
        The {{GPUStencilOperation}} performed if the fragment stencil comparison test described by
        {{GPUStencilFaceState/compare}} passes.
</dl>

<script type=idl>
enum GPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "invert",
    "increment-clamp",
    "decrement-clamp",
    "increment-wrap",
    "decrement-wrap",
};
</script>

{{GPUStencilOperation}} defines the following operations:

<dl dfn-type=enum-value dfn-for=GPUStencilOperation>
    : <dfn>"keep"</dfn>
    ::
        Keep the current stencil value.

    : <dfn>"zero"</dfn>
    ::
        Set the stencil value to `0`.

    : <dfn>"replace"</dfn>
    ::
        Set the stencil value to {{RenderState/[[stencilReference]]}}.

    : <dfn>"invert"</dfn>
    ::
        Bitwise-invert the current stencil value.

    : <dfn>"increment-clamp"</dfn>
    ::
        Increments the current stencil value, clamping to the maximum representable value of the
        {{GPURenderPassDescriptor/depthStencilAttachment}}'s stencil aspect.

    : <dfn>"decrement-clamp"</dfn>
    ::
        Decrement the current stencil value, clamping to `0`.

    : <dfn>"increment-wrap"</dfn>
    ::
        Increments the current stencil value, wrapping to zero if the value exceeds the maximum
        representable value of the {{GPURenderPassDescriptor/depthStencilAttachment}}'s stencil
        aspect.

    : <dfn>"decrement-wrap"</dfn>
    ::
        Decrement the current stencil value, wrapping to the maximum representable value of the
        {{GPURenderPassDescriptor/depthStencilAttachment}}'s stencil aspect if the value goes below
        `0`.
</dl>

### Vertex State ### {#vertex-state}

<script type=idl>
enum GPUIndexFormat {
    "uint16",
    "uint32",
};
</script>

The index format determines both the data type of index values in a buffer and, when used with
strip primitive topologies ({{GPUPrimitiveTopology/"line-strip"}} or
{{GPUPrimitiveTopology/"triangle-strip"}}) also specifies the primitive restart value. The
<dfn dfn>primitive restart value</dfn> indicates which index value indicates that a new primitive
should be started rather than continuing to construct the triangle strip with the prior indexed
vertices.

{{GPUPrimitiveState}}s that specify a strip primitive topology must specify a
{{GPUPrimitiveState/stripIndexFormat}} if they are used for indexed draws
so that the [=primitive restart value=] that will be used is known at pipeline
creation time. {{GPUPrimitiveState}}s that specify a list primitive
topology will use the index format passed to {{GPURenderCommandsMixin/setIndexBuffer()}}
when doing indexed rendering.

<table class=data>
    <thead>
        <tr>
            <th>Index format
            <th>Byte size
            <th>Primitive restart value
    </thead>
    <tbody dfn-type=enum-value dfn-for=GPUIndexFormat>
        <tr>
            <td><dfn>"uint16"</dfn>
            <td>2
            <td>0xFFFF
        <tr>
            <td><dfn>"uint32"</dfn>
            <td>4
            <td>0xFFFFFFFF
    </tbody>
</table>

#### Vertex Formats #### {#vertex-formats}

The {{GPUVertexFormat}} of a vertex attribute indicates how data from a vertex buffer will
be interpreted and exposed to the shader. The name of the format specifies the order of components,
bits per component, and [=vertex data type=] for the component.

Each <dfn dfn>vertex data type</dfn> can map to any [=WGSL scalar type=] of the same base type,
regardless of the bits per component:

<table class=data>
    <thead>
        <tr>
            <th>Vertex format prefix
            <th>Vertex data type
            <th>Compatible WGSL types
    </thead>
    <tbody>
        <tr>
            <td>`uint`
            <td>unsigned int
            <td>`u32`
        <tr>
            <td>`sint`
            <td>signed int
            <td>`i32`
        <tr>
            <td>`unorm`
            <td>unsigned normalized
            <td rowspan=3>`f16`, `f32`
        <tr>
            <td>`snorm`
            <td>signed normalized
        <tr>
            <td>`float`
            <td>floating point
    </tbody>
</table>

The multi-component formats specify the number of components after "x". Mismatches in the number of
components between the vertex format and shader type are allowed, with components being either
dropped or filled with default values to compensate.

<div class=example>
    A vertex attribute with a format of {{GPUVertexFormat/"unorm8x2"}} and byte values `[0x7F, 0xFF]`
    can be accessed in the shader with the following types:

    <table class=data>
        <thead>
            <tr>
                <th>Shader type
                <th>Shader value
        </thead>
        <tbody>
            <tr>
                <td><code>f16</code>
                <td><code>0.5h</code>
            <tr>
                <td><code>f32</code>
                <td><code>0.5f</code>
            <tr>
                <td><code>vec2&lt;f16&gt;</code>
                <td><code>vec2(0.5h, 1.0h)</code>
            <tr>
                <td><code>vec2&lt;f32&gt;</code>
                <td><code>vec2(0.5f, 1.0f)</code>
            <tr>
                <td><code>vec3&lt;f16&gt;</code>
                <td><code>vec2(0.5h, 1.0h, 0.0h)</code>
            <tr>
                <td><code>vec3&lt;f32&gt;</code>
                <td><code>vec2(0.5f, 1.0f, 0.0f)</code>
            <tr>
                <td><code>vec4&lt;f16&gt;</code>
                <td><code>vec2(0.5h, 1.0h, 0.0h, 1.0h)</code>
            <tr>
                <td><code>vec4&lt;f32&gt;</code>
                <td><code>vec2(0.5f, 1.0f, 0.0f, 1.0f)</code>
        </tbody>
    </table>
</div>

See [[#vertex-processing]] for additional information about how vertex formats are exposed in the
shader.

<script type=idl>
enum GPUVertexFormat {
    "uint8",
    "uint8x2",
    "uint8x4",
    "sint8",
    "sint8x2",
    "sint8x4",
    "unorm8",
    "unorm8x2",
    "unorm8x4",
    "snorm8",
    "snorm8x2",
    "snorm8x4",
    "uint16",
    "uint16x2",
    "uint16x4",
    "sint16",
    "sint16x2",
    "sint16x4",
    "unorm16",
    "unorm16x2",
    "unorm16x4",
    "snorm16",
    "snorm16x2",
    "snorm16x4",
    "float16",
    "float16x2",
    "float16x4",
    "float32",
    "float32x2",
    "float32x3",
    "float32x4",
    "uint32",
    "uint32x2",
    "uint32x3",
    "uint32x4",
    "sint32",
    "sint32x2",
    "sint32x3",
    "sint32x4",
    "unorm10-10-10-2",
    "unorm8x4-bgra",
};
</script>

<table class=data>
    <thead>
        <tr>
            <th>Vertex format
            <th>Data type
            <th>Components
            <th><dfn abstract-op for=GPUVertexFormat>byteSize</dfn>
            <th>Example WGSL type
    </thead>
    <tbody dfn-type=enum-value dfn-for=GPUVertexFormat>
        <tr>
            <td><dfn>"uint8"</dfn>
            <td>unsigned int
            <td>1
            <td>1
            <td><code>u32</code>
        <tr>
            <td><dfn>"uint8x2"</dfn>
            <td>unsigned int
            <td>2
            <td>2
            <td><code>vec2&lt;u32&gt;</code>
        <tr>
            <td><dfn>"uint8x4"</dfn>
            <td>unsigned int
            <td>4
            <td>4
            <td><code>vec4&lt;u32&gt;</code>
        <tr>
            <td><dfn>"sint8"</dfn>
            <td>signed int
            <td>1
            <td>1
            <td><code>i32</code>
        <tr>
            <td><dfn>"sint8x2"</dfn>
            <td>signed int
            <td>2
            <td>2
            <td><code>vec2&lt;i32&gt;</code>
        <tr>
            <td><dfn>"sint8x4"</dfn>
            <td>signed int
            <td>4
            <td>4
            <td><code>vec4&lt;i32&gt;</code>
        <tr>
            <td><dfn>"unorm8"</dfn>
            <td>unsigned normalized
            <td>1
            <td>1
            <td><code>f32</code>
        <tr>
            <td><dfn>"unorm8x2"</dfn>
            <td>unsigned normalized
            <td>2
            <td>2
            <td><code>vec2&lt;f32&gt;</code>
        <tr>
            <td><dfn>"unorm8x4"</dfn>
            <td>unsigned normalized
            <td>4
            <td>4
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"snorm8"</dfn>
            <td>signed normalized
            <td>1
            <td>1
            <td><code>f32</code>
        <tr>
            <td><dfn>"snorm8x2"</dfn>
            <td>signed normalized
            <td>2
            <td>2
            <td><code>vec2&lt;f32&gt;</code>
        <tr>
            <td><dfn>"snorm8x4"</dfn>
            <td>signed normalized
            <td>4
            <td>4
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"uint16"</dfn>
            <td>unsigned int
            <td>1
            <td>2
            <td><code>u32</code>
        <tr>
            <td><dfn>"uint16x2"</dfn>
            <td>unsigned int
            <td>2
            <td>4
            <td><code>vec2&lt;u32&gt;</code>
        <tr>
            <td><dfn>"uint16x4"</dfn>
            <td>unsigned int
            <td>4
            <td>8
            <td><code>vec4&lt;u32&gt;</code>
        <tr>
            <td><dfn>"sint16"</dfn>
            <td>signed int
            <td>1
            <td>2
            <td><code>i32</code>
        <tr>
            <td><dfn>"sint16x2"</dfn>
            <td>signed int
            <td>2
            <td>4
            <td><code>vec2&lt;i32&gt;</code>
        <tr>
            <td><dfn>"sint16x4"</dfn>
            <td>signed int
            <td>4
            <td>8
            <td><code>vec4&lt;i32&gt;</code>
        <tr>
            <td><dfn>"unorm16"</dfn>
            <td>unsigned normalized
            <td>1
            <td>2
            <td><code>f32</code>
        <tr>
            <td><dfn>"unorm16x2"</dfn>
            <td>unsigned normalized
            <td>2
            <td>4
            <td><code>vec2&lt;f32&gt;</code>
        <tr>
            <td><dfn>"unorm16x4"</dfn>
            <td>unsigned normalized
            <td>4
            <td>8
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"snorm16"</dfn>
            <td>signed normalized
            <td>1
            <td>2
            <td><code>f32</code>
        <tr>
            <td><dfn>"snorm16x2"</dfn>
            <td>signed normalized
            <td>2
            <td>4
            <td><code>vec2&lt;f32&gt;</code>
        <tr>
            <td><dfn>"snorm16x4"</dfn>
            <td>signed normalized
            <td>4
            <td>8
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"float16"</dfn>
            <td>float
            <td>1
            <td>2
            <td><code>f32</code>
        <tr>
            <td><dfn>"float16x2"</dfn>
            <td>float
            <td>2
            <td>4
            <td><code>vec2&lt;f16&gt;</code>
        <tr>
            <td><dfn>"float16x4"</dfn>
            <td>float
            <td>4
            <td>8
            <td><code>vec4&lt;f16&gt;</code>
        <tr>
            <td><dfn>"float32"</dfn>
            <td>float
            <td>1
            <td>4
            <td><code>f32</code>
        <tr>
            <td><dfn>"float32x2"</dfn>
            <td>float
            <td>2
            <td>8
            <td><code>vec2&lt;f32&gt;</code>
        <tr>
            <td><dfn>"float32x3"</dfn>
            <td>float
            <td>3
            <td>12
            <td><code>vec3&lt;f32&gt;</code>
        <tr>
            <td><dfn>"float32x4"</dfn>
            <td>float
            <td>4
            <td>16
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"uint32"</dfn>
            <td>unsigned int
            <td>1
            <td>4
            <td><code>u32</code>
        <tr>
            <td><dfn>"uint32x2"</dfn>
            <td>unsigned int
            <td>2
            <td>8
            <td><code>vec2&lt;u32&gt;</code>
        <tr>
            <td><dfn>"uint32x3"</dfn>
            <td>unsigned int
            <td>3
            <td>12
            <td><code>vec3&lt;u32&gt;</code>
        <tr>
            <td><dfn>"uint32x4"</dfn>
            <td>unsigned int
            <td>4
            <td>16
            <td><code>vec4&lt;u32&gt;</code>
        <tr>
            <td><dfn>"sint32"</dfn>
            <td>signed int
            <td>1
            <td>4
            <td><code>i32</code>
        <tr>
            <td><dfn>"sint32x2"</dfn>
            <td>signed int
            <td>2
            <td>8
            <td><code>vec2&lt;i32&gt;</code>
        <tr>
            <td><dfn>"sint32x3"</dfn>
            <td>signed int
            <td>3
            <td>12
            <td><code>vec3&lt;i32&gt;</code>
        <tr>
            <td><dfn>"sint32x4"</dfn>
            <td>signed int
            <td>4
            <td>16
            <td><code>vec4&lt;i32&gt;</code>
        <tr>
            <td><dfn>"unorm10-10-10-2"</dfn>
            <td>unsigned normalized
            <td>4
            <td>4
            <td><code>vec4&lt;f32&gt;</code>
        <tr>
            <td><dfn>"unorm8x4-bgra"</dfn>
            <td>unsigned normalized
            <td>4
            <td>4
            <td><code>vec4&lt;f32&gt;</code>
    </tbody>
</table>

<script type=idl>
enum GPUVertexStepMode {
    "vertex",
    "instance",
};
</script>

The step mode configures how an address for vertex buffer data is computed, based on the
current vertex or instance index:

<dl dfn-type=enum-value dfn-for=GPUVertexStepMode>
    : <dfn>"vertex"</dfn>
    ::
        The address is advanced by {{GPUVertexBufferLayout/arrayStride}} for each vertex,
        and reset between instances.

    : <dfn>"instance"</dfn>
    ::
        The address is advanced by {{GPUVertexBufferLayout/arrayStride}} for each instance.
</dl>

<script type=idl>
dictionary GPUVertexState
         : GPUProgrammableStage {
    sequence<GPUVertexBufferLayout?> buffers = [];
};
</script>

<dl dfn-type=dict-member dfn-for=GPUVertexState>
    : <dfn>buffers</dfn>
    ::
        A list of {{GPUVertexBufferLayout}}s, each defining the layout of vertex attribute data in a
        vertex buffer used by this pipeline.
</dl>

A <dfn dfn noexport>vertex buffer</dfn> is, conceptually, a view into buffer memory as an *array of structures*.
{{GPUVertexBufferLayout/arrayStride}} is the stride, in bytes, between *elements* of that array.
Each element of a vertex buffer is like a *structure* with a memory layout defined by its
{{GPUVertexBufferLayout/attributes}}, which describe the *members* of the structure.

Each {{GPUVertexAttribute}} describes its
{{GPUVertexAttribute/format}} and its
{{GPUVertexAttribute/offset}}, in bytes, within the structure.

Each attribute appears as a separate input in a vertex shader, each bound by a numeric *location*,
which is specified by {{GPUVertexAttribute/shaderLocation}}.
Every location must be unique within the {{GPUVertexState}}.

<script type=idl>
dictionary GPUVertexBufferLayout {
    required GPUSize64 arrayStride;
    GPUVertexStepMode stepMode = "vertex";
    required sequence<GPUVertexAttribute> attributes;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUVertexBufferLayout>
    : <dfn>arrayStride</dfn>
    ::
        The stride, in bytes, between elements of this array.

    : <dfn>stepMode</dfn>
    ::
        Whether each element of this array represents per-vertex data or per-instance data

    : <dfn>attributes</dfn>
    ::
        An array defining the layout of the vertex attributes within each element.
</dl>

<script type=idl>
dictionary GPUVertexAttribute {
    required GPUVertexFormat format;
    required GPUSize64 offset;

    required GPUIndex32 shaderLocation;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUVertexAttribute>
    : <dfn>format</dfn>
    ::
        The {{GPUVertexFormat}} of the attribute.

    : <dfn>offset</dfn>
    ::
        The offset, in bytes, from the beginning of the element to the data for the attribute.

    : <dfn>shaderLocation</dfn>
    ::
        The numeric location associated with this attribute, which will correspond with a
        <a href="https://gpuweb.github.io/gpuweb/wgsl/#input-output-locations">"@location" attribute</a>
        declared in the {{GPURenderPipelineDescriptor/vertex}}.{{GPUProgrammableStage/module|module}}.
</dl>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUVertexBufferLayout</dfn>(device, descriptor)

    **Arguments:**

    - {{GPUDevice}} |device|
    - {{GPUVertexBufferLayout}} |descriptor|

    [=Device timeline=] steps:

    1. Return `true`, if and only if, all of the following conditions are satisfied:

        <div class=validusage>
            - |descriptor|.{{GPUVertexBufferLayout/arrayStride}} &le;
                |device|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexBufferArrayStride}}.
            - |descriptor|.{{GPUVertexBufferLayout/arrayStride}} is a multiple of 4.
            - For each attribute |attrib| in the list |descriptor|.{{GPUVertexBufferLayout/attributes}}:
                - If |descriptor|.{{GPUVertexBufferLayout/arrayStride}} is zero:
                    - |attrib|.{{GPUVertexAttribute/offset}} + [$GPUVertexFormat/byteSize$](|attrib|.{{GPUVertexAttribute/format}}) &le;
                        |device|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexBufferArrayStride}}.

                    Otherwise:

                    - |attrib|.{{GPUVertexAttribute/offset}} + [$GPUVertexFormat/byteSize$](|attrib|.{{GPUVertexAttribute/format}}) &le;
                        |descriptor|.{{GPUVertexBufferLayout/arrayStride}}.
                - |attrib|.{{GPUVertexAttribute/offset}} is a multiple of the minimum of 4 and
                    [$GPUVertexFormat/byteSize$](|attrib|.{{GPUVertexAttribute/format}}).
                - |attrib|.{{GPUVertexAttribute/shaderLocation}} is &lt;
                    |device|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexAttributes}}.
        </div>
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>validating GPUVertexState</dfn>(device, descriptor, layout)

    **Arguments:**

    - {{GPUDevice}} |device|
    - {{GPUVertexState}} |descriptor|
    - {{GPUPipelineLayout}} |layout|

    [=Device timeline=] steps:

    1. Let |entryPoint| be [$get the entry point$]({{GPUShaderStage/VERTEX}}, |descriptor|).
    1. [=Assert=] |entryPoint| is not `null`.
    1. All of the requirements in the following steps |must| be met.

        <div class=validusage>
            1. [$validating GPUProgrammableStage$]({{GPUShaderStage/VERTEX}}, |descriptor|, |layout|, |device|) |must| succeed.
            1. |descriptor|.{{GPUVertexState/buffers}}.[=list/size=] |must| be &le;
                |device|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexBuffers}}.
            1. Each |vertexBuffer| layout descriptor in the list |descriptor|.{{GPUVertexState/buffers}}
                |must| pass [$validating GPUVertexBufferLayout$](|device|, |vertexBuffer|).
            1. The sum of |vertexBuffer|.{{GPUVertexBufferLayout/attributes}}.[=list/size=],
                over every |vertexBuffer| in |descriptor|.{{GPUVertexState/buffers}},
                |must| be &le;
                |device|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexAttributes}}.
            1. For every vertex attribute declaration (at location |location| with type |T|) that is
                [=statically used=] by |entryPoint|, there |must| be exactly one pair (|i|, |j|) for which
                |descriptor|.{{GPUVertexState/buffers}}[|i|]?.{{GPUVertexBufferLayout/attributes}}[|j|].{{GPUVertexAttribute/shaderLocation}} == |location|.

                Let |attrib| be that {{GPUVertexAttribute}}.
            1. |T| |must| be compatible with |attrib|.{{GPUVertexAttribute/format}}'s [=vertex data type=]:
                <dl class=switch>
                    : "unorm", "snorm", or "float"
                    :: |T| must be `f32` or `vecN<f32>`.
                    : "uint"
                    :: |T| must be `u32` or `vecN<u32>`.
                    : "sint"
                    :: |T| must be `i32` or `vecN<i32>`.
                </dl>
        </div>
</div>

<pre class=include>
path: sections/copies.bs
</pre>

# Command Buffers # {#command-buffers}

Command buffers are pre-recorded lists of [=GPU commands=] (blocks of [=queue timeline=]
steps) that can be submitted to a {{GPUQueue}} for execution.
Each <dfn dfn>GPU command</dfn> represents a task to be performed on the
[=queue timeline=], such as setting state, drawing, copying resources, etc.

A {{GPUCommandBuffer}} can only be submitted once, at which point it becomes [$invalidated$].
To reuse rendering commands across multiple submissions, use {{GPURenderBundle}}.

<h3 id=gpucommandbuffer data-dfn-type=interface>`GPUCommandBuffer`
<span id=command-buffer></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUCommandBuffer {
};
GPUCommandBuffer includes GPUObjectBase;
</script>

{{GPUCommandBuffer}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUCommandBuffer data-timeline=device>
    : <dfn>\[[command_list]]</dfn>, of type [=list=]&lt;[=GPU command=]&gt;, readonly
    ::
        A [=list=] of [=GPU commands=] to be executed on the [=Queue timeline=] when this command
        buffer is submitted.

    : <dfn>\[[renderState]]</dfn>, of type [=RenderState=], initially `null`
    ::
        The current state used by any render pass commands being executed.
</dl>

### Command Buffer Creation ### {#command-buffer-creation}

<script type=idl>
dictionary GPUCommandBufferDescriptor
         : GPUObjectDescriptorBase {
};
</script>


# Command Encoding # {#command-encoding}

<h3 id=gpucommandsmixin data-dfn-type=interface>`GPUCommandsMixin`
<span id=commans-mixin></span>
</h3>

{{GPUCommandsMixin}} defines state common to all interfaces which encode commands.
It has no methods.

<script type=idl>
interface mixin GPUCommandsMixin {
};
</script>

{{GPUCommandsMixin}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUCommandsMixin data-timeline=device>
    : <dfn>\[[state]]</dfn>, of type [=encoder state=], initially "[=encoder state/open=]"
    ::
        The current state of the encoder.

    : <dfn>\[[commands]]</dfn>, of type [=list=]&lt;[=GPU command=]&gt;, initially `[]`
    ::
        A [=list=] of [=GPU commands=] to be executed on the [=Queue timeline=] when a
        {{GPUCommandBuffer}} containing these commands is submitted.
</dl>

The <dfn dfn>encoder state</dfn> may be one of the following:

<dl dfn-type=dfn dfn-for="encoder state">
    : "<dfn>open</dfn>"
    ::
        The encoder is available to encode new commands.

    : "<dfn>locked</dfn>"
    ::
        The encoder cannot be used, because it is locked by a child encoder: it is a
        {{GPUCommandEncoder}}, and a {{GPURenderPassEncoder}} or {{GPUComputePassEncoder}} is active.
        The encoder becomes "[=encoder state/open=]" again when the pass is ended.

        Any command issued in this state [$invalidates$] the encoder.

    : "<dfn>ended</dfn>"
    ::
        The encoder has been ended and new commands can no longer be encoded.

        Any command issued in this state will [$generate a validation error$].
</dl>

<div algorithm class=validusage data-timeline=device>
    To <dfn abstract-op>Validate the encoder state</dfn> of {{GPUCommandsMixin}} |encoder| run the <br/>
    following [=device timeline=] steps:

    1. If |encoder|.{{GPUCommandsMixin/[[state]]}} is:
        <dl class=switch>
            : "[=encoder state/open=]"
            :: Return `true`.

            : "[=encoder state/locked=]"
            :: [$Invalidate$] |encoder| and return `false`.

            : "[=encoder state/ended=]"
            :: [$Generate a validation error$], and return `false`.
        </dl>
</div>

<div algorithm data-timeline=device>
    To <dfn abstract-op>Enqueue a command</dfn> on {{GPUCommandsMixin}} |encoder|
    which issues the steps of a [=GPU Command=] |command|, run the following [=device timeline=] steps:

        1. [=list/Append=] |command| to |encoder|.{{GPUCommandsMixin/[[commands]]}}.
        1. When |command| is executed as part of a {{GPUCommandBuffer}}:
            1. Issue the steps of |command|.
</div>

<h3 id=gpucommandencoder data-dfn-type=interface>`GPUCommandEncoder`
<span id=command-encoder></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUCommandEncoder {
    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
    GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});

    undefined copyBufferToBuffer(
        GPUBuffer source,
        GPUBuffer destination,
        optional GPUSize64 size);
    undefined copyBufferToBuffer(
        GPUBuffer source,
        GPUSize64 sourceOffset,
        GPUBuffer destination,
        GPUSize64 destinationOffset,
        optional GPUSize64 size);

    undefined copyBufferToTexture(
        GPUTexelCopyBufferInfo source,
        GPUTexelCopyTextureInfo destination,
        GPUExtent3D copySize);

    undefined copyTextureToBuffer(
        GPUTexelCopyTextureInfo source,
        GPUTexelCopyBufferInfo destination,
        GPUExtent3D copySize);

    undefined copyTextureToTexture(
        GPUTexelCopyTextureInfo source,
        GPUTexelCopyTextureInfo destination,
        GPUExtent3D copySize);

    undefined clearBuffer(
        GPUBuffer buffer,
        optional GPUSize64 offset = 0,
        optional GPUSize64 size);

    undefined resolveQuerySet(
        GPUQuerySet querySet,
        GPUSize32 firstQuery,
        GPUSize32 queryCount,
        GPUBuffer destination,
        GPUSize64 destinationOffset);

    GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
};
GPUCommandEncoder includes GPUObjectBase;
GPUCommandEncoder includes GPUCommandsMixin;
GPUCommandEncoder includes GPUDebugCommandsMixin;
</script>

### Command Encoder Creation ### {#command-encoder-creation}

<script type=idl>
dictionary GPUCommandEncoderDescriptor
         : GPUObjectDescriptorBase {
};
</script>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createCommandEncoder(descriptor)</dfn>
    ::
        Creates a {{GPUCommandEncoder}}.

        <div algorithm=GPUDevice.createCommandEncoder>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createCommandEncoder(descriptor)">
                    |descriptor|: Description of the {{GPUCommandEncoder}} to create.
                </pre>

                **Returns:** {{GPUCommandEncoder}}

                [=Content timeline=] steps:

                1. Let |e| be [=!=] [$create a new WebGPU object$](|this|, {{GPUCommandEncoder}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |e|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |e| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                    </div>
            </div>
        </div>
</dl>

<div class=example>
    Creating a {{GPUCommandEncoder}}, encoding a command to clear a buffer, finishing the
    encoder to get a {{GPUCommandBuffer}}, then submitting it to the {{GPUQueue}}.

    <pre highlight=js>
        const commandEncoder = gpuDevice.createCommandEncoder();
        commandEncoder.clearBuffer(buffer);
        const commandBuffer = commandEncoder.finish();
        gpuDevice.queue.submit([commandBuffer]);
    </pre>
</div>

## Pass Encoding ## {#command-encoder-pass-encoding}

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>beginRenderPass(descriptor)</dfn>
    ::
        Begins encoding a render pass described by |descriptor|.

        <div algorithm=GPUCommandEncoder.beginRenderPass>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/beginRenderPass(descriptor)">
                    |descriptor|: Description of the {{GPURenderPassEncoder}} to create.
                </pre>

                **Returns:** {{GPURenderPassEncoder}}

                [=Content timeline=] steps:

                1. For each non-`null` |colorAttachment| in |descriptor|.{{GPURenderPassDescriptor/colorAttachments}}:
                    1. If |colorAttachment|.{{GPURenderPassColorAttachment/clearValue}} is [=map/exists|provided=]:
                        1. [=?=] [$validate GPUColor shape$](|colorAttachment|.{{GPURenderPassColorAttachment/clearValue}}).
                1. Let |pass| be a new {{GPURenderPassEncoder}} object.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |pass|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. [$Validate the encoder state$] of |this|.
                    If it returns false, [$invalidate$] |pass| and return.
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/locked=]".
                1. Let |attachmentRegions| be a [=list=] of [[=texture subresource=], {{GPURenderPassColorAttachment/depthSlice}}?]
                    pairs, initially empty. Each pair describes the region of the texture to be rendered to, which
                    includes a single depth slice for {{GPUTextureViewDimension/"3d"}} textures only.
                1. For each non-`null` |colorAttachment| in |descriptor|.{{GPURenderPassDescriptor/colorAttachments}}:
                    1. Add [|colorAttachment|.{{GPURenderPassColorAttachment/view}},
                        |colorAttachment|.{{GPURenderPassColorAttachment/depthSlice}} ?? `null`] to |attachmentRegions|.
                    1. If |colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}} is not `null`:
                        1. Add [|colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}},
                            `undefined`] to |attachmentRegions|.
                1. If any of the following requirements are unmet, [$invalidate$] |pass| and return.
                    <div class=validusage>
                        - |descriptor| must meet the [$GPURenderPassDescriptor/Valid Usage$] rules
                            given device |this|.{{GPUObjectBase/[[device]]}}.
                        - The set of texture regions in |attachmentRegions| must be pairwise disjoint.
                            That is, no two texture regions may overlap.
                    </div>
                1. [$usage scope/Add$] each [=texture subresource=] in |attachmentRegions|
                    to |pass|.{{GPURenderCommandsMixin/[[usage scope]]}}
                    with usage [=internal usage/attachment=].
                1. Let |depthStencilAttachment| be |descriptor|.{{GPURenderPassDescriptor/depthStencilAttachment}}.
                1. If |depthStencilAttachment| is not `null`:
                    1. Let |depthStencilView| be |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/view}}.
                    1. [$usage scope/Add$] the [=aspect/depth=] [=subresource=] of |depthStencilView|, if any,
                        to |pass|.{{GPURenderCommandsMixin/[[usage scope]]}}
                        with usage [=internal usage/attachment-read=] if
                        |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthReadOnly}} is true,
                        or [=internal usage/attachment=] otherwise.
                    1. [$usage scope/Add$] the [=aspect/stencil=] [=subresource=] of |depthStencilView|, if any,
                        to |pass|.{{GPURenderCommandsMixin/[[usage scope]]}}
                        with usage [=internal usage/attachment-read=] if
                        |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilReadOnly}} is true,
                        or [=internal usage/attachment=] otherwise.
                    1. Set |pass|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} to |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthReadOnly}}.
                    1. Set |pass|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} to |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilReadOnly}}.
                1. Set |pass|.{{GPURenderCommandsMixin/[[layout]]}} to [$derive render targets layout from pass$](|descriptor|).
                1. If |descriptor|.{{GPURenderPassDescriptor/timestampWrites}} is [=map/exist|provided=]:
                    1. Let |timestampWrites| be |descriptor|.{{GPURenderPassDescriptor/timestampWrites}}.
                    1. If |timestampWrites|.{{GPURenderPassTimestampWrites/beginningOfPassWriteIndex}}
                        is [=map/exist|provided=],
                        [=list/append=] a [=GPU command=] to |this|.{{GPUCommandsMixin/[[commands]]}}
                        with the following steps:

                        <div data-timeline=queue>
                            1. Before the pass commands begin executing,
                                write the [$current queue timestamp$] into index
                                |timestampWrites|.{{GPURenderPassTimestampWrites/beginningOfPassWriteIndex}}
                                of |timestampWrites|.{{GPURenderPassTimestampWrites/querySet}}.
                        </div>
                    1. If |timestampWrites|.{{GPURenderPassTimestampWrites/endOfPassWriteIndex}}
                        is [=map/exist|provided=], set |pass|.{{GPURenderPassEncoder/[[endTimestampWrite]]}}
                        to a [=GPU command=] with the following steps:

                        <div data-timeline=queue>
                            1. After the pass commands finish executing,
                                write the [$current queue timestamp$] into index
                                |timestampWrites|.{{GPURenderPassTimestampWrites/endOfPassWriteIndex}}
                                of |timestampWrites|.{{GPURenderPassTimestampWrites/querySet}}.
                        </div>
                1. Set |pass|.{{GPURenderCommandsMixin/[[drawCount]]}} to 0.
                1. Set |pass|.{{GPURenderPassEncoder/[[maxDrawCount]]}} to |descriptor|.{{GPURenderPassDescriptor/maxDrawCount}}.
                1. Set |pass|.{{GPURenderPassEncoder/[[maxDrawCount]]}} to |descriptor|.{{GPURenderPassDescriptor/maxDrawCount}}.

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let the {{GPUCommandBuffer/[[renderState]]}} of the currently executing
                    {{GPUCommandBuffer}} be a new [=RenderState=].

                1. Set {{GPUCommandBuffer/[[renderState]]}}.{{RenderState/[[colorAttachments]]}} to
                    |descriptor|.{{GPURenderPassDescriptor/colorAttachments}}.
                1. Set {{GPUCommandBuffer/[[renderState]]}}.{{RenderState/[[depthStencilAttachment]]}} to
                    |descriptor|.{{GPURenderPassDescriptor/depthStencilAttachment}}.

                1. For each non-`null` |colorAttachment| in |descriptor|.{{GPURenderPassDescriptor/colorAttachments}}:
                    1. Let |colorView| be |colorAttachment|.{{GPURenderPassColorAttachment/view}}.
                    1. If |colorView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/dimension}} is:
                        <dl class=switch>
                            : {{GPUTextureViewDimension/"3d"}}
                            ::
                                Let |colorSubregion| be |colorAttachment|.{{GPURenderPassColorAttachment/depthSlice}} of
                                |colorView|.

                            : Otherwise
                            ::
                                Let |colorSubregion| be |colorView|.
                        </dl>

                    1. If |colorAttachment|.{{GPURenderPassColorAttachment/loadOp}} is:
                        <dl class=switch>
                            : {{GPULoadOp/"load"}}
                            ::
                                Ensure the contents of |colorSubregion| are loaded into the [=framebuffer memory=]
                                associated with |colorSubregion|.

                            : {{GPULoadOp/"clear"}}
                            ::
                                Set every [=texel block|texel=] of the [=framebuffer memory=] associated with
                                |colorSubregion| to |colorAttachment|.{{GPURenderPassColorAttachment/clearValue}}.
                        </dl>

                1. If |depthStencilAttachment| is not `null`:
                    1. If |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthLoadOp}} is:
                        <dl class=switch>
                            : Not [=map/exist|provided=]
                            ::
                                [=Assert=] that |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthReadOnly}}
                                is `true` and ensure the contents of the [=aspect/depth=] [=GPUTextureView/subresource=]
                                of |depthStencilView| are loaded into the [=framebuffer memory=] associated with
                                |depthStencilView|.

                            : {{GPULoadOp/"load"}}
                            ::
                                Ensure the contents of the [=aspect/depth=] [=GPUTextureView/subresource=] of
                                |depthStencilView| are loaded into the [=framebuffer memory=] associated with
                                |depthStencilView|.

                            : {{GPULoadOp/"clear"}}
                            ::
                                Set every [=texel block|texel=] of the [=framebuffer memory=] associated with the
                                [=aspect/depth=] [=GPUTextureView/subresource=] of |depthStencilView| to
                                |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthClearValue}}.
                        </dl>

                    1. If |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilLoadOp}} is:
                        <dl class=switch>
                            : Not [=map/exist|provided=]
                            ::
                                [=Assert=] that |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilReadOnly}}
                                is `true` and ensure the contents of the [=aspect/stencil=] [=GPUTextureView/subresource=]
                                of |depthStencilView| are loaded into the [=framebuffer memory=] associated with
                                |depthStencilView|.

                            : {{GPULoadOp/"load"}}
                            ::
                                Ensure the contents of the [=aspect/stencil=] [=GPUTextureView/subresource=] of
                                |depthStencilView| are loaded into the [=framebuffer memory=] associated with
                                |depthStencilView|.

                            : {{GPULoadOp/"clear"}}
                            ::
                                Set every [=texel block|texel=] of the [=framebuffer memory=] associated with the
                                [=aspect/stencil=] [=GPUTextureView/subresource=] |depthStencilView| to
                                |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilClearValue}}.
                        </dl>

                Note: [=Read-only depth-stencil=] attachments are implicitly treated as though the {{GPULoadOp/"load"}}
                operation was used. Validation that requires the load op to not be provided for read-only attachments
                is done in [$GPURenderPassDepthStencilAttachment/GPURenderPassDepthStencilAttachment Valid Usage$].
            </div>
        </div>

    : <dfn>beginComputePass(descriptor)</dfn>
    ::
        Begins encoding a compute pass described by |descriptor|.

        <div algorithm=GPUCommandEncoder.beginComputePass>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/beginComputePass(descriptor)">
                    descriptor:
                </pre>

                **Returns:** {{GPUComputePassEncoder}}

                [=Content timeline=] steps:

                1. Let |pass| be a new {{GPUComputePassEncoder}} object.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |pass|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. [$Validate the encoder state$] of |this|.
                    If it returns false, [$invalidate$] |pass| and return.
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/locked=]".
                1. If any of the following requirements are unmet, [$invalidate$] |pass| and return.
                    <div class=validusage>
                        - If |descriptor|.{{GPUComputePassDescriptor/timestampWrites}} is [=map/exist|provided=]:
                            - [$Validate timestampWrites$](|this|.{{GPUObjectBase/[[device]]}},
                                |descriptor|.{{GPUComputePassDescriptor/timestampWrites}})
                                must return true.
                    </div>
                1. If |descriptor|.{{GPUComputePassDescriptor/timestampWrites}} is [=map/exist|provided=]:
                    1. Let |timestampWrites| be |descriptor|.{{GPUComputePassDescriptor/timestampWrites}}.
                    1. If |timestampWrites|.{{GPUComputePassTimestampWrites/beginningOfPassWriteIndex}}
                        is [=map/exist|provided=],
                        [=list/append=] a [=GPU command=] to |this|.{{GPUCommandsMixin/[[commands]]}}
                        with the following steps:

                        <div data-timeline=queue>
                            1. Before the pass commands begin executing,
                                write the [$current queue timestamp$] into index
                                |timestampWrites|.{{GPUComputePassTimestampWrites/beginningOfPassWriteIndex}}
                                of |timestampWrites|.{{GPUComputePassTimestampWrites/querySet}}.
                        </div>
                    1. If |timestampWrites|.{{GPUComputePassTimestampWrites/endOfPassWriteIndex}}
                        is [=map/exist|provided=], set |pass|.{{GPUComputePassEncoder/[[endTimestampWrite]]}}
                        to a [=GPU command=] with the following steps:

                        <div data-timeline=queue>
                            1. After the pass commands finish executing,
                                write the [$current queue timestamp$] into index
                                |timestampWrites|.{{GPUComputePassTimestampWrites/endOfPassWriteIndex}}
                                of |timestampWrites|.{{GPUComputePassTimestampWrites/querySet}}.
                        </div>
            </div>
        </div>
</dl>

## Buffer Copy Commands ## {#commands-buffer-copies}

<dfn dfn for=GPUCommandEncoder>copyBufferToBuffer()</dfn> has two overloads:

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>copyBufferToBuffer(source, destination, size)</dfn>
    ::
        Shorthand, equivalent to {{GPUCommandEncoder/copyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size)|copyBufferToBuffer(source, 0, destination, 0, size)}}.

    : <dfn>copyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size)</dfn>
    ::
        Encode a command into the {{GPUCommandEncoder}} that copies data from a sub-region of a
        {{GPUBuffer}} to a sub-region of another {{GPUBuffer}}.

        <div algorithm=GPUCommandEncoder.copyBufferToBuffer>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/copyBufferToBuffer(source, sourceOffset, destination, destinationOffset, size)">
                    |source|: The {{GPUBuffer}} to copy from.
                    |sourceOffset|: Offset in bytes into |source| to begin copying from.
                    |destination|: The {{GPUBuffer}} to copy to.
                    |destinationOffset|: Offset in bytes into |destination| to place the copied data.
                    |size|: Bytes to copy.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If |size| is `undefined`, set it to |source|.{{GPUBuffer/size}} &minus; |sourceOffset|.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |source| is [$valid to use with$] |this|.
                        - |destination| is [$valid to use with$] |this|.
                        - |source|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/COPY_SRC}}.
                        - |destination|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/COPY_DST}}.
                        - |size| is a multiple of 4.
                        - |sourceOffset| is a multiple of 4.
                        - |destinationOffset| is a multiple of 4.
                        - |source|.{{GPUBuffer/size}} &ge; (|sourceOffset| + |size|).
                        - |destination|.{{GPUBuffer/size}} &ge; (|destinationOffset| + |size|).
                        - |source| and |destination| are not the same {{GPUBuffer}}.
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Copy |size| bytes of |source|, beginning at |sourceOffset|, into |destination|,
                    beginning at |destinationOffset|.
            </div>
        </div>
</dl>

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>clearBuffer(buffer, offset, size)</dfn>
    ::
        Encode a command into the {{GPUCommandEncoder}} that fills a sub-region of a
        {{GPUBuffer}} with zeros.

        <div algorithm=GPUCommandEncoder.clearBuffer>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/clearBuffer(buffer, offset, size)">
                    |buffer|: The {{GPUBuffer}} to clear.
                    |offset|: Offset in bytes into |buffer| where the sub-region to clear begins.
                    |size|: Size in bytes of the sub-region to clear. Defaults to the size of the buffer minus |offset|.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If |size| is missing, set |size| to <code>max(0, |buffer|.{{GPUBuffer/size}} - |offset|)</code>.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |buffer| is [$valid to use with$] |this|.
                        - |buffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/COPY_DST}}.
                        - |size| is a multiple of 4.
                        - |offset| is a multiple of 4.
                        - |buffer|.{{GPUBuffer/size}} &ge; (|offset| + |size|).
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Set |size| bytes of |buffer| to `0` starting at |offset|.
            </div>

        </div>
</dl>

<h3 id=commands-texel-copies>Texel Copy Commands
<span id=commands-image-copies></span>
</h3>

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>copyBufferToTexture(source, destination, copySize)</dfn>
    ::
        Encode a command into the {{GPUCommandEncoder}} that copies data from a sub-region of a
        {{GPUBuffer}} to a sub-region of one or multiple continuous [=texture subresources=].

        <div algorithm=GPUCommandEncoder.copyBufferToTexture>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/copyBufferToTexture(source, destination, copySize)">
                    |source|: Combined with |copySize|, defines the region of the source buffer.
                    |destination|: Combined with |copySize|, defines the region of the destination [=texture subresource=].
                    |copySize|:
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUOrigin3D shape$](|destination|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUExtent3D shape$](|copySize|).
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}:
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |aligned| be `true`.
                1. Let |dataLength| be |source|.{{GPUTexelCopyBufferInfo/buffer}}.{{GPUBuffer/size}}.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - [$validating GPUTexelCopyBufferInfo$](|source|) returns `true`.
                        - |source|.{{GPUTexelCopyBufferInfo/buffer}}.{{GPUBuffer/usage}} contains
                            {{GPUBufferUsage/COPY_SRC}}.
                        - [$validating texture buffer copy$](|destination|, |source|, |dataLength|, |copySize|, {{GPUTextureUsage/COPY_DST}}, |aligned|) returns `true`.
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |blockWidth| be the [=texel block width=] of |destination|.{{GPUTexelCopyTextureInfo/texture}}.
                1. Let |blockHeight| be the [=texel block height=] of |destination|.{{GPUTexelCopyTextureInfo/texture}}.

                1. Let |dstOrigin| be |destination|.{{GPUTexelCopyTextureInfo/origin}}.
                1. Let |dstBlockOriginX| be (|dstOrigin|.[=GPUOrigin3D/x=] &div; |blockWidth|).
                1. Let |dstBlockOriginY| be (|dstOrigin|.[=GPUOrigin3D/y=] &div; |blockHeight|).

                1. Let |blockColumns| be (|copySize|.[=GPUExtent3D/width=] &div; |blockWidth|).
                1. Let |blockRows| be (|copySize|.[=GPUExtent3D/height=] &div; |blockHeight|).

                1. [=Assert=] that |dstBlockOriginX|, |dstBlockOriginY|, |blockColumns|, and |blockRows| are integers.

                1. For each |z| in the range [0, |copySize|.[=GPUExtent3D/depthOrArrayLayers=] &minus; 1]:
                    1. Let |dstSubregion| be [$texture copy sub-region$] (|z| &plus; |dstOrigin|.[=GPUOrigin3D/z=]) of |destination|.

                    1. For each |y| in the range [0, |blockRows| &minus; 1]:
                        1. For each |x| in the range [0, |blockColumns| &minus; 1]:
                            1. Let |blockOffset| be the [$texel block byte offset$] of |source| for (|x|, |y|, |z|) of
                                |destination|.{{GPUTexelCopyTextureInfo/texture}}.

                            1. Set [=texel block=] (|dstBlockOriginX| &plus; |x|, |dstBlockOriginY| &plus; |y|) of
                                |dstSubregion| to be an [=equivalent texel representation=] to the [=texel block=]
                                described by |source|.{{GPUTexelCopyBufferInfo/buffer}} at offset |blockOffset|.
            </div>

        </div>

    : <dfn>copyTextureToBuffer(source, destination, copySize)</dfn>
    ::
        Encode a command into the {{GPUCommandEncoder}} that copies data from a sub-region of one or
        multiple continuous [=texture subresources=] to a sub-region of a {{GPUBuffer}}.

        <div algorithm=GPUCommandEncoder.copyTextureToBuffer>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/copyTextureToBuffer(source, destination, copySize)">
                    |source|: Combined with |copySize|, defines the region of the source [=texture subresources=].
                    |destination|: Combined with |copySize|, defines the region of the destination buffer.
                    |copySize|:
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUOrigin3D shape$](|source|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUExtent3D shape$](|copySize|).
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}:
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |aligned| be `true`.
                1. Let |dataLength| be |destination|.{{GPUTexelCopyBufferInfo/buffer}}.{{GPUBuffer/size}}.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - [$validating GPUTexelCopyBufferInfo$](|destination|) returns `true`.
                        - |destination|.{{GPUTexelCopyBufferInfo/buffer}}.{{GPUBuffer/usage}} contains
                            {{GPUBufferUsage/COPY_DST}}.
                        - [$validating texture buffer copy$](|source|, |destination|, |dataLength|, |copySize|, {{GPUTextureUsage/COPY_SRC}}, |aligned|) returns `true`.
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |blockWidth| be the [=texel block width=] of |source|.{{GPUTexelCopyTextureInfo/texture}}.
                1. Let |blockHeight| be the [=texel block height=] of |source|.{{GPUTexelCopyTextureInfo/texture}}.

                1. Let |srcOrigin| be |source|.{{GPUTexelCopyTextureInfo/origin}}.
                1. Let |srcBlockOriginX| be (|srcOrigin|.[=GPUOrigin3D/x=] &div; |blockWidth|).
                1. Let |srcBlockOriginY| be (|srcOrigin|.[=GPUOrigin3D/y=] &div; |blockHeight|).

                1. Let |blockColumns| be (|copySize|.[=GPUExtent3D/width=] &div; |blockWidth|).
                1. Let |blockRows| be (|copySize|.[=GPUExtent3D/height=] &div; |blockHeight|).

                1. [=Assert=] that |srcBlockOriginX|, |srcBlockOriginY|, |blockColumns|, and |blockRows| are integers.

                1. For each |z| in the range [0, |copySize|.[=GPUExtent3D/depthOrArrayLayers=] &minus; 1]:
                    1. Let |srcSubregion| be [$texture copy sub-region$] (|z| &plus; |srcOrigin|.[=GPUOrigin3D/z=]) of |source|.

                    1. For each |y| in the range [0, |blockRows| &minus; 1]:
                        1. For each |x| in the range [0, |blockColumns| &minus; 1]:
                            1. Let |blockOffset| be the [$texel block byte offset$] of |destination| for (|x|, |y|, |z|) of
                                |source|.{{GPUTexelCopyTextureInfo/texture}}.

                            1. Set |destination|.{{GPUTexelCopyBufferInfo/buffer}} at offset |blockOffset| to be an
                                [=equivalent texel representation=] to [=texel block=]
                                (|srcBlockOriginX| &plus; |x|, |srcBlockOriginY| &plus; |y|) of |srcSubregion|.
            </div>

        </div>

    : <dfn>copyTextureToTexture(source, destination, copySize)</dfn>
    ::
        Encode a command into the {{GPUCommandEncoder}} that copies data from a sub-region of one
        or multiple contiguous [=texture subresources=] to another sub-region of one or
        multiple continuous [=texture subresources=].

        <div algorithm=GPUCommandEncoder.copyTextureToTexture>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/copyTextureToTexture(source, destination, copySize)">
                    |source|: Combined with |copySize|, defines the region of the source [=texture subresources=].
                    |destination|: Combined with |copySize|, defines the region of the destination [=texture subresources=].
                    |copySize|:
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUOrigin3D shape$](|source|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUOrigin3D shape$](|destination|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUExtent3D shape$](|copySize|).
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}:
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - Let |srcTexture| be |source|.{{GPUTexelCopyTextureInfo/texture}}.
                        - Let |dstTexture| be |destination|.{{GPUTexelCopyTextureInfo/texture}}.
                        - [$validating GPUTexelCopyTextureInfo$](|source|, |copySize|) returns `true`.
                        - |srcTexture|.{{GPUTexture/usage}} contains {{GPUTextureUsage/COPY_SRC}}.
                        - [$validating GPUTexelCopyTextureInfo$](|destination|, |copySize|) returns `true`.
                        - |dstTexture|.{{GPUTexture/usage}} contains {{GPUTextureUsage/COPY_DST}}.
                        - |srcTexture|.{{GPUTexture/sampleCount}} is equal to |dstTexture|.{{GPUTexture/sampleCount}}.
                        - |srcTexture|.{{GPUTexture/format}} and |dstTexture|.{{GPUTexture/format}}
                            must be [=copy-compatible=].
                        - If |srcTexture|.{{GPUTexture/format}} is a depth-stencil format:
                            - |source|.{{GPUTexelCopyTextureInfo/aspect}} and |destination|.{{GPUTexelCopyTextureInfo/aspect}}
                                must both refer to all aspects of |srcTexture|.{{GPUTexture/format}}
                                and |dstTexture|.{{GPUTexture/format}}, respectively.
                        - The [$set of subresources for texture copy$](|source|, |copySize|) and
                            the [$set of subresources for texture copy$](|destination|, |copySize|) are disjoint.
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |blockWidth| be the [=texel block width=] of |source|.{{GPUTexelCopyTextureInfo/texture}}.
                1. Let |blockHeight| be the [=texel block height=] of |source|.{{GPUTexelCopyTextureInfo/texture}}.

                1. Let |srcOrigin| be |source|.{{GPUTexelCopyTextureInfo/origin}}.
                1. Let |srcBlockOriginX| be (|srcOrigin|.[=GPUOrigin3D/x=] &div; |blockWidth|).
                1. Let |srcBlockOriginY| be (|srcOrigin|.[=GPUOrigin3D/y=] &div; |blockHeight|).

                1. Let |dstOrigin| be |destination|.{{GPUTexelCopyTextureInfo/origin}}.
                1. Let |dstBlockOriginX| be (|dstOrigin|.[=GPUOrigin3D/x=] &div; |blockWidth|).
                1. Let |dstBlockOriginY| be (|dstOrigin|.[=GPUOrigin3D/y=] &div; |blockHeight|).

                1. Let |blockColumns| be (|copySize|.[=GPUExtent3D/width=] &div; |blockWidth|).
                1. Let |blockRows| be (|copySize|.[=GPUExtent3D/height=] &div; |blockHeight|).

                1. [=Assert=] that |srcBlockOriginX|, |srcBlockOriginY|, |dstBlockOriginX|, |dstBlockOriginY|,
                    |blockColumns|, and |blockRows| are integers.

                1. For each |z| in the range [0, |copySize|.[=GPUExtent3D/depthOrArrayLayers=] &minus; 1]:
                    1. Let |srcSubregion| be [$texture copy sub-region$] (|z| &plus; |srcOrigin|.[=GPUOrigin3D/z=]) of |source|.
                    1. Let |dstSubregion| be [$texture copy sub-region$] (|z| &plus; |dstOrigin|.[=GPUOrigin3D/z=]) of |destination|.

                    1. For each |y| in the range [0, |blockRows| &minus; 1]:
                        1. For each |x| in the range [0, |blockColumns| &minus; 1]:
                            1. Set [=texel block=] (|dstBlockOriginX| &plus; |x|, |dstBlockOriginY| &plus; |y|) of
                                |dstSubregion| to be an [=equivalent texel representation=] to [=texel block=]
                                (|srcBlockOriginX| &plus; |x|, |srcBlockOriginY| &plus; |y|) of |srcSubregion|.
            </div>

        </div>
</dl>

## Queries ## {#command-encoder-queries}

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset)</dfn>
    ::
        Resolves query results from a {{GPUQuerySet}} out into a range of a {{GPUBuffer}}.

        <div algorithm=GPUCommandEncoder.resolveQuerySet>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/resolveQuerySet(querySet, firstQuery, queryCount, destination, destinationOffset)">
                    querySet:
                    firstQuery:
                    queryCount:
                    destination:
                    destinationOffset:
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                        |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |querySet| is [$valid to use with$] |this|.
                        - |destination| is [$valid to use with$] |this|.
                        - |destination|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/QUERY_RESOLVE}}.
                        - |firstQuery| &lt; the number of queries in |querySet|.
                        - (|firstQuery| + |queryCount|) &le; the number of queries in |querySet|.
                        - |destinationOffset| is a multiple of 256.
                        - |destinationOffset| + 8 &times; |queryCount| &le; |destination|.{{GPUBuffer/size}}.
                    </div>

                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |queryIndex| be |firstQuery|.
                1. Let |offset| be |destinationOffset|.
                1. While |queryIndex| &lt; |firstQuery| + |queryCount|:
                    1. Set 8 bytes of |destination|, beginning at |offset|, to be the value of
                        |querySet| at |queryIndex|.
                    1. Set |queryIndex| to be |queryIndex| + 1.
                    1. Set |offset| to be |offset| + 8.
            </div>
        </div>
</dl>

## Finalization ## {#command-encoder-finalization}

A {{GPUCommandBuffer}} containing the commands recorded by the {{GPUCommandEncoder}} can be created
by calling {{GPUCommandEncoder/finish()}}. Once {{GPUCommandEncoder/finish()}} has been called the
command encoder can no longer be used.

<dl dfn-type=method dfn-for=GPUCommandEncoder>
    : <dfn>finish(descriptor)</dfn>
    ::
        Completes recording of the commands sequence and returns a corresponding {{GPUCommandBuffer}}.

        <div algorithm=GPUCommandEncoder.finish>
            <div data-timeline=content>
                **Called on:** {{GPUCommandEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCommandEncoder/finish(descriptor)">
                    descriptor:
                </pre>

                **Returns:** {{GPUCommandBuffer}}

                [=Content timeline=] steps:

                1. Let |commandBuffer| be a new {{GPUCommandBuffer}}.
                1. Issue the |finish steps| on the [=Device timeline=] of
                            |this|.{{GPUObjectBase/[[device]]}}.
                1. Return |commandBuffer|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |finish steps|:

                1. Let |validationSucceeded| be `true` if all of the following requirements are met, and `false` otherwise.

                    <div class=validusage>
                        - |this| must be [$valid$].
                        - |this|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/open=]".
                        - |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}} must [=list/is empty|be empty=].
                    </div>
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/ended=]".
                1. If |validationSucceeded| is `false`, then:
                    1. [$Generate a validation error$].
                    1. Return an [$invalidated$] {{GPUCommandBuffer}}.
                1. Set |commandBuffer|.{{GPUCommandBuffer/[[command_list]]}} to
                    |this|.{{GPUCommandsMixin/[[commands]]}}.
            </div>
        </div>
</dl>

# Programmable Passes # {#programmable-passes}

<script type=idl>
interface mixin GPUBindingCommandsMixin {
    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);

    undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
        [AllowShared] Uint32Array dynamicOffsetsData,
        GPUSize64 dynamicOffsetsDataStart,
        GPUSize32 dynamicOffsetsDataLength);
};
</script>

{{GPUBindingCommandsMixin}} assumes the presence of
{{GPUObjectBase}} and {{GPUCommandsMixin}} members on the same object.
It must only be included by interfaces which also include those mixins.

{{GPUBindingCommandsMixin}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUBindingCommandsMixin data-timeline=device>
    : <dfn>\[[bind_groups]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, {{GPUBindGroup}}&gt;, initially empty
    ::
        The current {{GPUBindGroup}} for each index.

    : <dfn>\[[dynamic_offsets]]</dfn>, of type [=ordered map=]&lt;{{GPUIndex32}}, [=list=]&lt;{{GPUBufferDynamicOffset}}&gt;&gt;, initally empty
    ::
        The current dynamic offsets for each {{GPUBindingCommandsMixin/[[bind_groups]]}} entry.
</dl>

## Bind Groups ## {#programmable-passes-bind-groups}

<dfn dfn for=GPUBindingCommandsMixin>setBindGroup()</dfn> has two overloads:

<dl dfn-type=method dfn-for=GPUBindingCommandsMixin>
    : <dfn>setBindGroup(index, bindGroup, dynamicOffsets)</dfn>
    ::
        Sets the current {{GPUBindGroup}} for the given index.

        <div algorithm=GPUBindingCommandsMixin.setBindGroup>
            <div data-timeline=content>
                **Called on:** {{GPUBindingCommandsMixin}} this.

                **Arguments:**

                <!-- TODO(tabatkins/bikeshed#1740, plinss/widlparser#56):
                The argumentdef feature doesn't work with overloaded functions, and it ends up
                expecting this to define the arguments for the 5-arg variant of the method, despite
                the "for" explicitly pointing at the 3-arg variant.
                So, we don't use argumentdef for this method. -->

                <dl dfn-type=argument dfn-for="GPUBindingCommandsMixin/setBindGroup(index, bindGroup, dynamicOffsets)">
                    : <dfn>|index|</dfn>, of type {{GPUIndex32}}, non-nullable, required
                    ::
                        The index to set the bind group at.

                    : <dfn>|bindGroup|</dfn>, of type {{GPUBindGroup}}, nullable, required
                    ::
                        Bind group to use for subsequent render or compute commands.

                    : <dfn>|dynamicOffsets|</dfn>, of type [=sequence=]&lt;{{GPUBufferDynamicOffset}}&gt;, non-nullable, defaulting to `[]`
                    ::
                        Array containing buffer offsets in bytes for each entry in
                        |bindGroup| marked as {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}},
                        ordered by {{GPUBindGroupLayoutEntry}}.{{GPUBindGroupLayoutEntry/binding}}.
                        See [note](#dynamicOffsetOrder) for additional details.
                </dl>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |dynamicOffsetCount| be 0 if `bindGroup` is `null`, or
                    |bindGroup|.{{GPUBindGroup/[[layout]]}}.{{GPUBindGroupLayout/[[dynamicOffsetCount]]}} if not.
                1. If any of the following requirements are unmet, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |index| must be &lt;
                            |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxBindGroups}}.
                        - |dynamicOffsets|.[=list/size=] must equal |dynamicOffsetCount|.
                    </div>
                1. If |bindGroup| is `null`:
                    1. [=map/Remove=] |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|].
                    1. [=map/Remove=] |this|.{{GPUBindingCommandsMixin/[[dynamic_offsets]]}}[|index|].

                    Otherwise:

                    1. If any of the following requirements are unmet, [$invalidate$] |this| and return.

                        <div class=validusage>
                            - |bindGroup| must be [$valid to use with$] |this|.
                            - [$Iterate over each dynamic binding offset|For each dynamic binding$]
                                (|bufferBinding|, |bufferLayout|, |dynamicOffsetIndex|) in |bindGroup|:
                                - |bufferBinding|.{{GPUBufferBinding/offset}} + |dynamicOffsets|[|dynamicOffsetIndex|] +
                                    |bufferLayout|.{{GPUBufferBindingLayout/minBindingSize}} must be &le;
                                    |bufferBinding|.{{GPUBufferBinding/buffer}}.{{GPUBuffer/size}}.
                                - If |bufferLayout|.{{GPUBufferBindingLayout/type}} is {{GPUBufferBindingType/"uniform"}}:

                                    - |dynamicOffset| must be a multiple of {{supported limits/minUniformBufferOffsetAlignment}}.

                                - If |bufferLayout|.{{GPUBufferBindingLayout/type}} is {{GPUBufferBindingType/"storage"}}
                                    or {{GPUBufferBindingType/"read-only-storage"}}:

                                    - |dynamicOffset| must be a multiple of {{supported limits/minStorageBufferOffsetAlignment}}.
                        </div>
                    1. Set |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|] to be |bindGroup|.
                    1. Set |this|.{{GPUBindingCommandsMixin/[[dynamic_offsets]]}}[|index|] to be a copy of |dynamicOffsets|.
                    1. If |this| is a {{GPURenderCommandsMixin}}:
                        1. For each |bindGroup| in |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}},
                            [$usage scope/merge$] |bindGroup|.{{GPUBindGroup/[[usedResources]]}}
                            into |this|.{{GPURenderCommandsMixin/[[usage scope]]}}
            </div>
        </div>

    : <dfn>setBindGroup(index, bindGroup, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength)</dfn>
    ::
        Sets the current {{GPUBindGroup}} for the given index, specifying dynamic offsets as a subset
        of a {{Uint32Array}}.

        <div algorithm=GPUBindingCommandsMixin.setBindGroup2>
            <div data-timeline=content>
                **Called on:** {{GPUBindingCommandsMixin}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUBindingCommandsMixin/setBindGroup(index, bindGroup, dynamicOffsetsData, dynamicOffsetsDataStart, dynamicOffsetsDataLength)">
                    |index|: The index to set the bind group at.
                    |bindGroup|: Bind group to use for subsequent render or compute commands.
                    |dynamicOffsetsData|: Array containing buffer offsets in bytes for each entry in
                        |bindGroup| marked as {{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}},
                        ordered by {{GPUBindGroupLayoutEntry}}.{{GPUBindGroupLayoutEntry/binding}}.
                        See [note](#dynamicOffsetOrder) for additional details.
                    |dynamicOffsetsDataStart|: Offset in elements into |dynamicOffsetsData| where the
                        buffer offset data begins.
                    |dynamicOffsetsDataLength|: Number of buffer offsets to read from |dynamicOffsetsData|.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. If any of the following requirements are unmet, throw a {{RangeError}} and return.

                    <div class=validusage>
                        - |dynamicOffsetsDataStart| must be &ge; 0.
                        - |dynamicOffsetsDataStart| + |dynamicOffsetsDataLength| must be &le;
                            |dynamicOffsetsData|.`length`.
                    </div>
                1. Let |dynamicOffsets| be a [=list=] containing the range, starting at index
                    |dynamicOffsetsDataStart|, of |dynamicOffsetsDataLength| elements of
                    [=get a copy of the buffer source|a copy of=] |dynamicOffsetsData|.
                1. Call |this|.{{GPUBindingCommandsMixin/setBindGroup(index,
                    bindGroup, dynamicOffsets)|setBindGroup}}(|index|, |bindGroup|, |dynamicOffsets|).
            </div>
        </div>
</dl>

<div class=note heading id=dynamicOffsetOrder>
    Dynamic offset are applied in {{GPUBindGroupLayoutEntry}}.{{GPUBindGroupLayoutEntry/binding}} order.

    This means that if `dynamic bindings` is the list of each {{GPUBindGroupLayoutEntry}} in the {{GPUBindGroupLayout}}
    with {{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} set to `true`, sorted by
    {{GPUBindGroupLayoutEntry}}.{{GPUBindGroupLayoutEntry/binding}}, then `dynamic offset[i]`, as supplied to
    [=GPUBindingCommandsMixin/setBindGroup()=], will correspond to `dynamic bindings[i]`.

    <div class=example>
        For a {{GPUBindGroupLayout}} created with the following call:

        <pre highlight=js>
            // Note the bindings are listed out-of-order in this array, but it
            // doesn't matter because they will be sorted by binding index.
            let layout = gpuDevice.createBindGroupLayout({
                entries: [{
                    binding: 1,
                    buffer: {},
                }, {
                    binding: 2,
                    buffer: { dynamicOffset: true },
                }, {
                    binding: 0,
                    buffer: { dynamicOffset: true },
                }]
            });
        </pre>

        Used by a {{GPUBindGroup}} created with the following call:

        <pre highlight=js>
            // Like above, the array order doesn't matter here.
            // It doesn't even need to match the order used in the layout.
            let bindGroup = gpuDevice.createBindGroup({
                layout: layout,
                entries: [{
                    binding: 1,
                    resource: { buffer: bufferA, offset: 256 },
                }, {
                    binding: 2,
                    resource: { buffer: bufferB, offset: 512 },
                }, {
                    binding: 0,
                    resource: { buffer: bufferC },
                }]
            });
        </pre>

        And bound with the following call:

        <pre highlight=js>
            pass.setBindGroup(0, bindGroup, [1024, 2048]);
        </pre>

        The following buffer offsets will be applied:

        <table class=data>
            <thead>
                <tr><th>Binding <th>Buffer <th>Offset
            </thead>
            <tr><td>0 <td>bufferC <td>1024 (Dynamic)
            <tr><td>1 <td>bufferA <td>256 (Static)
            <tr><td>2 <td>bufferB <td>2560 (Static + Dynamic)
        </table>
    </div>
</div>


<div algorithm data-timeline=device>
    To <dfn abstract-op>Iterate over each dynamic binding offset</dfn> in a given {{GPUBindGroup}} |bindGroup|
    with a given list of |steps| to be executed for each dynamic offset, run the following [=device timeline=] steps:

    1. Let |dynamicOffsetIndex| be `0`.
    1. Let |layout| be |bindGroup|.{{GPUBindGroup/[[layout]]}}.
    1. For each {{GPUBindGroupEntry}} |entry| in |bindGroup|.{{GPUBindGroup/[[entries]]}} ordered in increasing values of |entry|.{{GPUBindGroupEntry/binding}}:
        1. Let |bindingDescriptor| be the {{GPUBindGroupLayoutEntry}} at
            |layout|.{{GPUBindGroupLayout/[[entryMap]]}}[|entry|.{{GPUBindGroupEntry/binding}}]:
        1. If |bindingDescriptor|.{{GPUBindGroupLayoutEntry/buffer}}?.{{GPUBufferBindingLayout/hasDynamicOffset}} is `true`:
            1. Let |bufferBinding| be [$get as buffer binding$](|entry|.{{GPUBindGroupEntry/resource}}).
            1. Let |bufferLayout| be |bindingDescriptor|.{{GPUBindGroupLayoutEntry/buffer}}.
            1. Call |steps| with |bufferBinding|, |bufferLayout|, and |dynamicOffsetIndex|.
            1. Let |dynamicOffsetIndex| be |dynamicOffsetIndex| + `1`
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>Validate encoder bind groups</dfn>(encoder, pipeline)

    **Arguments:**

    : {{GPUBindingCommandsMixin}} |encoder|
    :: Encoder whose bind groups are being validated.
    : {{GPUPipelineBase}} |pipeline|
    :: Pipeline to validate |encoder|s bind groups are compatible with.

    [=Device timeline=] steps:

    1. If any of the following conditions are unsatisfied, return `false`:

        <div class=validusage>
            - |pipeline| must not be `null`.
            - All bind groups used by the pipeline must be set and compatible with the pipeline layout:
                For each pair of ({{GPUIndex32}} |index|, {{GPUBindGroupLayout}} |bindGroupLayout|) in
                |pipeline|.{{GPUPipelineBase/[[layout]]}}.{{GPUPipelineLayout/[[bindGroupLayouts]]}}:
                - If |bindGroupLayout| is `null`, [=iteration/continue=].
                - Let |bindGroup| be |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|index|].
                - Let |dynamicOffsets| be |encoder|.{{GPUBindingCommandsMixin/[[dynamic_offsets]]}}[|index|].
                - |bindGroup| must not be `null`.
                - |bindGroup|.{{GPUBindGroup/[[layout]]}} must be [=group-equivalent=] with |bindGroupLayout|.
                - Let |dynamicOffsetIndex| be 0.
                - For each {{GPUBindGroupEntry}} |bindGroupEntry| in |bindGroup|.{{GPUBindGroup/[[entries]]}},
                    sorted by |bindGroupEntry|.{{GPUBindGroupEntry/binding}}:
                    - Let |bindGroupLayoutEntry| be
                        |bindGroup|.{{GPUBindGroup/[[layout]]}}.{{GPUBindGroupLayout/[[entryMap]]}}[|bindGroupEntry|.{{GPUBindGroupEntry/binding}}].
                    - If |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/buffer}} is not
                        [=map/exists|provided=], **continue**.
                    - Let |bound| be [$get as buffer binding$](|bindGroupEntry|.{{GPUBindGroupEntry/resource}}).
                    - If |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/hasDynamicOffset}}:
                        - Increment |bound|.{{GPUBufferBinding/offset}} by
                            |dynamicOffsets|[|dynamicOffsetIndex|].
                        - Increment |dynamicOffsetIndex| by 1.
                    - If |bindGroupEntry|.{{GPUBindGroupEntry/[[prevalidatedSize]]}} is `false`:
                        - [$effective buffer binding size$](|bound|) must be &ge; [=minimum buffer binding size=]
                            of the binding variable in |pipeline|'s shader that corresponds to |bindGroupEntry|.
            - [$Encoder bind groups alias a writable resource$](|encoder|, |pipeline|) must be `false`.
        </div>

    Otherwise return `true`.
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>Encoder bind groups alias a writable resource</dfn>(|encoder|, |pipeline|)
    if any writable buffer binding range overlaps with any other binding range of the same buffer,
    or any writable texture binding overlaps in [=texture subresources=] with any other texture binding
    (which may use the same or a different {{GPUTextureView}} object).

    Note: This algorithm limits the use of the [=usage scope storage exception=].

    **Arguments:**

    : {{GPUBindingCommandsMixin}} |encoder|
    :: Encoder whose bind groups are being validated.
    : {{GPUPipelineBase}} |pipeline|
    :: Pipeline to validate |encoder|s bind groups are compatible with.

    [=Device timeline=] steps:

    1. For each |stage| in [{{GPUShaderStage/VERTEX}}, {{GPUShaderStage/FRAGMENT}}, {{GPUShaderStage/COMPUTE}}]:
        1. Let |bufferBindings| be a [=list=] of ({{GPUBufferBinding}}, {{boolean}}) pairs,
            where the latter indicates whether the resource was used as writable.
        1. Let |textureViews| be a [=list=] of ({{GPUTextureView}}, {{boolean}}) pairs,
            where the latter indicates whether the resource was used as writable.
        1. For each pair of ({{GPUIndex32}} |bindGroupIndex|, {{GPUBindGroupLayout}} |bindGroupLayout|) in
            |pipeline|.{{GPUPipelineBase/[[layout]]}}.{{GPUPipelineLayout/[[bindGroupLayouts]]}}:
            1. Let |bindGroup| be
                |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}[|bindGroupIndex|].
            1. Let |bindGroupLayoutEntries| be
                |bindGroupLayout|.{{GPUBindGroupLayout/[[descriptor]]}}.{{GPUBindGroupLayoutDescriptor/entries}}.
            1. Let |bufferRanges| be the [=GPUBindGroup/bound buffer ranges=] of |bindGroup|,
                given dynamic offsets
                |encoder|.{{GPUBindingCommandsMixin/[[dynamic_offsets]]}}[|bindGroupIndex|]
            1. For each ({{GPUBindGroupLayoutEntry}} |bindGroupLayoutEntry|,
                {{GPUBufferBinding}} |resource|) in |bufferRanges|, in which
                |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/visibility}} contains |stage|:
                1. Let |resourceWritable| be (|bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/buffer}}.{{GPUBufferBindingLayout/type}} == {{GPUBufferBindingType/"storage"}}).
                1. For each pair ({{GPUBufferBinding}} |pastResource|, {{boolean}} |pastResourceWritable|) in |bufferBindings|:
                    1. If (|resourceWritable| or |pastResourceWritable|) is true, and
                        |pastResource| and |resource| are [=buffer-binding-aliasing=], return `true`.
                1. [=list/append|Append=] (|resource|, |resourceWritable|) to |bufferBindings|.
            1. For each {{GPUBindGroupLayoutEntry}} |bindGroupLayoutEntry| in
                |bindGroupLayoutEntries|, and corresponding {{GPUTextureView}} |resource|
                in |bindGroup|, in which
                |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/visibility}} contains |stage|:
                1. If |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/storageTexture}} is not [=map/exist|provided=], **continue**.
                1. Let |resourceWritable| be whether
                    |bindGroupLayoutEntry|.{{GPUBindGroupLayoutEntry/storageTexture}}.{{GPUStorageTextureBindingLayout/access}}
                    is a writable access mode.
                1. For each pair ({{GPUTextureView}} |pastResource|, {{boolean}} |pastResourceWritable|) in |textureViews|,
                    1. If (|resourceWritable| or |pastResourceWritable|) is true, and
                        |pastResource| and |resource| is [=texture-view-aliasing=], return `true`.
                1. [=list/append|Append=] (|resource|, |resourceWritable|) to |textureViews|.
    1. Return `false`.

    Note:
    Implementations are strongly encouraged to optimize this algorithm.
</div>

# Debug Markers # {#debug-markers}

<dfn interface>GPUDebugCommandsMixin</dfn> provides methods to apply debug labels to groups
of commands or insert a single label into the command sequence.

Debug groups can be nested to create a hierarchy of labeled commands, and must be well-balanced.

Like {{GPUObjectBase/label|object labels}}, these labels have no required behavior, but may be shown
in error messages and browser developer tools, and may be passed to native API backends.

<script type=idl>
interface mixin GPUDebugCommandsMixin {
    undefined pushDebugGroup(USVString groupLabel);
    undefined popDebugGroup();
    undefined insertDebugMarker(USVString markerLabel);
};
</script>

{{GPUDebugCommandsMixin}} assumes the presence of
{{GPUObjectBase}} and {{GPUCommandsMixin}} members on the same object.
It must only be included by interfaces which also include those mixins.

{{GPUDebugCommandsMixin}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUDebugCommandsMixin data-timeline=device>
    : <dfn>\[[debug_group_stack]]</dfn>, of type [=stack=]&lt;{{USVString}}&gt;
    ::
        A stack of active debug group labels.
</dl>

{{GPUDebugCommandsMixin}} has the following methods:

<dl dfn-type=method dfn-for=GPUDebugCommandsMixin>
    : <dfn>pushDebugGroup(groupLabel)</dfn>
    ::
        Begins a labeled debug group containing subsequent commands.

        <div algorithm=GPUDebugCommandsMixin.pushDebugGroup>
            <div data-timeline=content>
                **Called on:** {{GPUDebugCommandsMixin}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDebugCommandsMixin/pushDebugGroup(groupLabel)">
                    |groupLabel|: The label for the command group.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. [=stack/Push=] |groupLabel| onto |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}}.
            </div>
        </div>

    : <dfn>popDebugGroup()</dfn>
    ::
        Ends the labeled debug group most recently started by {{GPUDebugCommandsMixin/pushDebugGroup()}}.

        <div algorithm=GPUDebugCommandsMixin.popDebugGroup>
            <div data-timeline=content>
                **Called on:** {{GPUDebugCommandsMixin}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following requirements are unmet, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}} must not [=list/is empty|be empty=].
                    </div>
                1. [=stack/Pop=] an entry off of |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}}.
            </div>
        </div>

    : <dfn>insertDebugMarker(markerLabel)</dfn>
    ::
        Marks a point in a stream of commands with a label.

        <div algorithm=GPUDebugCommandsMixin.insertDebugMarker>
            <div data-timeline=content>
                **Called on:** {{GPUDebugCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDebugCommandsMixin/insertDebugMarker(markerLabel)">
                    markerLabel: The label to insert.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
            </div>
        </div>
</dl>

# Compute Passes # {#compute-passes}

<h3 id=gpucomputepassencoder data-dfn-type=interface>`GPUComputePassEncoder`
<span id=compute-pass-encoder></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUComputePassEncoder {
    undefined setPipeline(GPUComputePipeline pipeline);
    undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
    undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);

    undefined end();
};
GPUComputePassEncoder includes GPUObjectBase;
GPUComputePassEncoder includes GPUCommandsMixin;
GPUComputePassEncoder includes GPUDebugCommandsMixin;
GPUComputePassEncoder includes GPUBindingCommandsMixin;
</script>

{{GPUComputePassEncoder}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUComputePassEncoder data-timeline=device>
    : <dfn>\[[command_encoder]]</dfn>, of type {{GPUCommandEncoder}}, readonly
    ::
        The {{GPUCommandEncoder}} that created this compute pass encoder.

    : <dfn>\[[endTimestampWrite]]</dfn>, of type [=GPU command=]?, readonly, defaulting to `null`
    ::
        [=GPU command=], if any, writing a timestamp when the pass ends.

    : <dfn>\[[pipeline]]</dfn>, of type {{GPUComputePipeline}}, initially `null`
    ::
        The current {{GPUComputePipeline}}.
</dl>

### Compute Pass Encoder Creation ### {#compute-pass-encoder-creation}

<script type=idl>
dictionary GPUComputePassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUComputePassTimestampWrites>
    : <dfn>querySet</dfn>
    ::
        The {{GPUQuerySet}}, of type {{GPUQueryType/"timestamp"}}, that the query results will be
        written to.

    : <dfn>beginningOfPassWriteIndex</dfn>
    ::
        If defined, indicates the query index in {{GPURenderPassTimestampWrites/querySet}} into
        which the timestamp at the beginning of the compute pass will be written.

    : <dfn>endOfPassWriteIndex</dfn>
    ::
        If defined, indicates the query index in {{GPURenderPassTimestampWrites/querySet}} into
        which the timestamp at the end of the compute pass will be written.
</dl>

Note: Timestamp query values are written in nanoseconds, but how the value is determined is
[=implementation-defined=] and may not increase monotonically. See [[#timestamp]] for details.

<script type=idl>
dictionary GPUComputePassDescriptor
         : GPUObjectDescriptorBase {
    GPUComputePassTimestampWrites timestampWrites;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUComputePassDescriptor>
    : <dfn>timestampWrites</dfn>
    ::
        Defines which timestamp values will be written for this pass, and where to write them to.
</dl>

### Dispatch ### {#compute-pass-encoder-dispatch}

<dl dfn-type=method dfn-for=GPUComputePassEncoder>
    : <dfn>setPipeline(pipeline)</dfn>
    ::
        Sets the current {{GPUComputePipeline}}.

        <div algorithm=GPUComputePassEncoder.setPipeline>
            <div data-timeline=content>
                **Called on:** {{GPUComputePassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUComputePassEncoder/setPipeline(pipeline)">
                    |pipeline|: The compute pipeline to use for subsequent dispatch commands.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |pipeline| is [$valid to use with$] |this|.
                    </div>
                1. Set |this|.{{GPUComputePassEncoder/[[pipeline]]}} to be |pipeline|.
            </div>
        </div>

    : <dfn>dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ)</dfn>
    ::
        Dispatch work to be performed with the current {{GPUComputePipeline}}.
        See [[#computing-operations]] for the detailed specification.

        <div algorithm=GPUComputePassEncoder.dispatch>
            <div data-timeline=content>
                **Called on:** {{GPUComputePassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUComputePassEncoder/dispatchWorkgroups(workgroupCountX, workgroupCountY, workgroupCountZ)">
                    |workgroupCountX|: X dimension of the grid of workgroups to dispatch.
                    |workgroupCountY|: Y dimension of the grid of workgroups to dispatch.
                    |workgroupCountZ|: Z dimension of the grid of workgroups to dispatch.
                </pre>

                <div class=note heading>
                    The `x`, `y`, and `z` values passed to {{GPUComputePassEncoder/dispatchWorkgroups()}}
                    and {{GPUComputePassEncoder/dispatchWorkgroupsIndirect()}} are the number of
                    *workgroups* to dispatch for each dimension, *not* the number of shader invocations
                    to perform across each dimension. This matches the behavior of modern native GPU
                    APIs, but differs from the behavior of OpenCL.

                    This means that if a {{GPUShaderModule}} defines an entry point with
                    `@workgroup_size(4, 4)`, and work is dispatched to it with the call
                    `computePass.dispatchWorkgroups(8, 8);` the entry point will be invoked 1024 times
                    total: Dispatching a 4x4 workgroup 8 times along both the X and Y axes.
                    (`4*4*8*8=1024`)
                </div>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |usageScope| be an empty [=usage scope=].
                1. For each |bindGroup| in |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}},
                    [$usage scope/merge$] |bindGroup|.{{GPUBindGroup/[[usedResources]]}}
                    into |this|.{{GPURenderCommandsMixin/[[usage scope]]}}
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |usageScope| must satisfy [=usage scope validation=].
                        - [$Validate encoder bind groups$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}})
                            is `true`.
                        - all of |workgroupCountX|, |workgroupCountY| and |workgroupCountZ| are &le;
                            |this|.device.limits.{{supported limits/maxComputeWorkgroupsPerDimension}}.
                    </div>

                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=].
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Execute a grid of workgroups with dimensions [|workgroupCountX|, |workgroupCountY|,
                    |workgroupCountZ|] with |bindingState|.{{GPUComputePassEncoder/[[pipeline]]}} using
                    |bindingState|.{{GPUBindingCommandsMixin/[[bind_groups]]}}.
            </div>
        </div>

    : <dfn>dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset)</dfn>
    ::
        Dispatch work to be performed with the current {{GPUComputePipeline}} using parameters read
        from a {{GPUBuffer}}.
        See [[#computing-operations]] for the detailed specification.

        The <dfn dfn for="">indirect dispatch parameters</dfn> encoded in the buffer must be a tightly
        packed block of **three 32-bit unsigned integer values (12 bytes total)**,
        given in the same order as the arguments for {{GPUComputePassEncoder/dispatchWorkgroups()}}.
        For example:

        <pre highlight=js>
            let dispatchIndirectParameters = new Uint32Array(3);
            dispatchIndirectParameters[0] = workgroupCountX;
            dispatchIndirectParameters[1] = workgroupCountY;
            dispatchIndirectParameters[2] = workgroupCountZ;
        </pre>

        <div algorithm=GPUComputePassEncoder.dispatchIndirect>
            <div data-timeline=content>
                **Called on:** {{GPUComputePassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUComputePassEncoder/dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset)">
                    |indirectBuffer|: Buffer containing the [=indirect dispatch parameters=].
                    |indirectOffset|: Offset in bytes into |indirectBuffer| where the dispatch data begins.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |usageScope| be an empty [=usage scope=].
                1. For each |bindGroup| in |this|.{{GPUBindingCommandsMixin/[[bind_groups]]}},
                    [$usage scope/merge$] |bindGroup|.{{GPUBindGroup/[[usedResources]]}}
                    into |this|.{{GPURenderCommandsMixin/[[usage scope]]}}
                1. [$usage scope/Add$] |indirectBuffer| to |usageScope|
                    with usage [=internal usage/input=].
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |usageScope| must satisfy [=usage scope validation=].
                        - [$Validate encoder bind groups$](|this|, |this|.{{GPUComputePassEncoder/[[pipeline]]}})
                            is `true`.
                        - |indirectBuffer| is [$valid to use with$] |this|.
                        - |indirectBuffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/INDIRECT}}.
                        - |indirectOffset| + sizeof([=indirect dispatch parameters=]) &le;
                            |indirectBuffer|.{{GPUBuffer/size}}.
                        - |indirectOffset| is a multiple of 4.
                    </div>
                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=].
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |workgroupCountX| be an unsigned 32-bit integer read from |indirectBuffer| at
                    |indirectOffset| bytes.
                1. Let |workgroupCountY| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 4) bytes.
                1. Let |workgroupCountZ| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 8) bytes.
                1. If |workgroupCountX|, |workgroupCountY|, or |workgroupCountZ| is greater than
                    |this|.device.limits.{{supported limits/maxComputeWorkgroupsPerDimension}},
                    return.
                1. Execute a grid of workgroups with dimensions [|workgroupCountX|, |workgroupCountY|,
                    |workgroupCountZ|] with |bindingState|.{{GPUComputePassEncoder/[[pipeline]]}} using
                    |bindingState|.{{GPUBindingCommandsMixin/[[bind_groups]]}}.
            </div>
        </div>
</dl>

### Finalization ### {#compute-pass-encoder-finalization}

The compute pass encoder can be ended by calling {{GPUComputePassEncoder/end()}} once the user
has finished recording commands for the pass. Once {{GPUComputePassEncoder/end()}} has been
called the compute pass encoder can no longer be used.

<dl dfn-type=method dfn-for=GPUComputePassEncoder>
    : <dfn>end()</dfn>
    ::
        Completes recording of the compute pass commands sequence.

        <div algorithm=GPUComputePassEncoder.end>
            <div data-timeline=content>
                **Called on:** {{GPUComputePassEncoder}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Let |parentEncoder| be |this|.{{GPURenderPassEncoder/[[command_encoder]]}}.
                1. If any of the following requirements are unmet,
                    [$generate a validation error$] and return.

                    <div class=validusage>
                        - |this|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/open=]".
                        - |parentEncoder|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/locked=]".
                    </div>
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/ended=]".
                1. Set |parentEncoder|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/open=]".
                1. If any of the following requirements are unmet, [$invalidate$] |parentEncoder| and return.

                    <div class=validusage>
                        - |this| must be [$valid$].
                        - |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}} must [=list/is empty|be empty=].
                    </div>
                1. [=list/Extend=] |parentEncoder|.{{GPUCommandsMixin/[[commands]]}}
                    with |this|.{{GPUCommandsMixin/[[commands]]}}.
                1. If |this|.{{GPUComputePassEncoder/[[endTimestampWrite]]}} is not `null`:
                    1. [=list/Extend=] |parentEncoder|.{{GPUCommandsMixin/[[commands]]}}
                        with |this|.{{GPUComputePassEncoder/[[endTimestampWrite]]}}.
            </div>
        </div>
</dl>

# Render Passes # {#render-passes}

<h3 id=gpurenderpassencoder data-dfn-type=interface>`GPURenderPassEncoder`
<span id=render-pass-encoder></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPURenderPassEncoder {
    undefined setViewport(float x, float y,
        float width, float height,
        float minDepth, float maxDepth);

    undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
                        GPUIntegerCoordinate width, GPUIntegerCoordinate height);

    undefined setBlendConstant(GPUColor color);
    undefined setStencilReference(GPUStencilValue reference);

    undefined beginOcclusionQuery(GPUSize32 queryIndex);
    undefined endOcclusionQuery();

    undefined executeBundles(sequence<GPURenderBundle> bundles);
    undefined end();
};
GPURenderPassEncoder includes GPUObjectBase;
GPURenderPassEncoder includes GPUCommandsMixin;
GPURenderPassEncoder includes GPUDebugCommandsMixin;
GPURenderPassEncoder includes GPUBindingCommandsMixin;
GPURenderPassEncoder includes GPURenderCommandsMixin;
</script>

{{GPURenderPassEncoder}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPURenderPassEncoder>
    : <dfn>\[[command_encoder]]</dfn>, of type {{GPUCommandEncoder}}, readonly
    ::
        The {{GPUCommandEncoder}} that created this render pass encoder.

    : <dfn>\[[attachment_size]]</dfn>, readonly
    ::
        Set to the following extents:

        - `width, height` = the dimensions of the pass's render attachments

    : <dfn>\[[occlusion_query_set]]</dfn>, of type {{GPUQuerySet}}, readonly
    ::
        The {{GPUQuerySet}} to store occlusion query results for the pass, which is initialized with
        {{GPURenderPassDescriptor}}.{{GPURenderPassDescriptor/occlusionQuerySet}} at pass creation time.

    : <dfn>\[[endTimestampWrite]]</dfn>, of type [=GPU command=]?, readonly, defaulting to `null`
    ::
        [=GPU command=], if any, writing a timestamp when the pass ends.

    : <dfn>\[[maxDrawCount]]</dfn> of type {{GPUSize64}}, readonly
    ::
        The maximum number of draws allowed in this pass.

    : <dfn>\[[occlusion_query_active]]</dfn>, of type {{boolean}}
    ::
        Whether the pass's {{GPURenderPassEncoder/[[occlusion_query_set]]}} is being written.
</dl>

When executing encoded render pass commands as part of a {{GPUCommandBuffer}}, an internal
<dfn dfn>RenderState</dfn> object is used to track the current state required for rendering.

[=RenderState=] has the following [=queue timeline properties=]:

<dl dfn-type=attribute dfn-for=RenderState data-timeline=queue>
    : <dfn>\[[occlusionQueryIndex]]</dfn>, of type {{GPUSize32}}
    ::
        The index into {{GPURenderPassEncoder/[[occlusion_query_set]]}} at which to store the
        occlusion query results.

    : <dfn>\[[viewport]]</dfn>
    ::  Current viewport rectangle and depth range. Initially set to the following values:
        - `x, y` = `0.0, 0.0`
        - `width, height` = the dimensions of the pass's render targets
        - `minDepth, maxDepth` = `0.0, 1.0`

    : <dfn>\[[scissorRect]]</dfn>
    ::  Current scissor rectangle. Initially set to the following values:
        - `x, y` = `0, 0`
        - `width, height` = the dimensions of the pass's render targets

    : <dfn>\[[blendConstant]]</dfn>, of type {{GPUColor}}
    ::  Current blend constant value, initially `[0, 0, 0, 0]`.

    : <dfn>\[[stencilReference]]</dfn>, of type {{GPUStencilValue}}
    ::  Current stencil reference value, initially `0`.

    : <dfn>\[[colorAttachments]]</dfn>, of type [=sequence=]&lt;{{GPURenderPassColorAttachment}}?&gt;
    ::  The color attachments and state for this render pass.

    : <dfn>\[[depthStencilAttachment]]</dfn>, of type {{GPURenderPassDepthStencilAttachment}}?
    ::  The depth/stencil attachment and state for this render pass.
</dl>

Render passes also have <dfn dfn>framebuffer memory</dfn>, which contains the [=texel block|texel=] data associated with
each attachment that is written into by draw commands and read from for blending and depth/stencil testing.

Note: Depending on the GPU hardware, [=framebuffer memory=] may be the memory allocated by the attachment textures or
may be a separate area of memory that the texture data is copied to and from, such as with tile-based architectures.

### Render Pass Encoder Creation ### {#render-pass-encoder-creation}

<script type=idl>
dictionary GPURenderPassTimestampWrites {
    required GPUQuerySet querySet;
    GPUSize32 beginningOfPassWriteIndex;
    GPUSize32 endOfPassWriteIndex;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderPassTimestampWrites>
    : <dfn>querySet</dfn>
    ::
        The {{GPUQuerySet}}, of type {{GPUQueryType/"timestamp"}}, that the query results will be
        written to.

    : <dfn>beginningOfPassWriteIndex</dfn>
    ::
        If defined, indicates the query index in {{GPURenderPassTimestampWrites/querySet}} into
        which the timestamp at the beginning of the render pass will be written.

    : <dfn>endOfPassWriteIndex</dfn>
    ::
        If defined, indicates the query index in {{GPURenderPassTimestampWrites/querySet}} into
        which the timestamp at the end of the render pass will be written.
</dl>

Note: Timestamp query values are written in nanoseconds, but how the value is determined is
[=implementation-defined=] and may not increase monotonically. See [[#timestamp]] for details.

<script type=idl>
dictionary GPURenderPassDescriptor
         : GPUObjectDescriptorBase {
    required sequence<GPURenderPassColorAttachment?> colorAttachments;
    GPURenderPassDepthStencilAttachment depthStencilAttachment;
    GPUQuerySet occlusionQuerySet;
    GPURenderPassTimestampWrites timestampWrites;
    GPUSize64 maxDrawCount = 50000000;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderPassDescriptor>
    : <dfn>colorAttachments</dfn>
    ::
        The set of {{GPURenderPassColorAttachment}} values in this sequence defines which
        color attachments will be output to when executing this render pass.

        Due to [=compatible usage list|usage compatibility=], no color attachment
        may alias another attachment or any resource used inside the render pass.

    : <dfn>depthStencilAttachment</dfn>
    ::
        The {{GPURenderPassDepthStencilAttachment}} value that defines the depth/stencil
        attachment that will be output to and tested against when executing this render pass.

        Due to [=compatible usage list|usage compatibility=], no writable depth/stencil attachment
        may alias another attachment or any resource used inside the render pass.

    : <dfn>occlusionQuerySet</dfn>
    ::
        The {{GPUQuerySet}} value defines where the occlusion query results will be stored for this pass.

    : <dfn>timestampWrites</dfn>
    ::
        Defines which timestamp values will be written for this pass, and where to write them to.

    : <dfn>maxDrawCount</dfn>
    ::
        The maximum number of draw calls that will be done in the render pass. Used by some
        implementations to size work injected before the render pass. Keeping the default value
        is a good default, unless it is known that more draw calls will be done.
</dl>

<div algorithm class=validusage dfn-for=GPURenderPassDescriptor data-timeline=device>
    <dfn abstract-op>Valid Usage</dfn>

    Given a {{GPUDevice}} |device| and {{GPURenderPassDescriptor}} |this|, the following validation rules apply:

    1. |this|.{{GPURenderPassDescriptor/colorAttachments}}.[=list/size=] must be &le;
        |device|.{{device/[[limits]]}}.{{supported limits/maxColorAttachments}}.

    1. For each non-`null` |colorAttachment| in |this|.{{GPURenderPassDescriptor/colorAttachments}}:

        1. |colorAttachment|.{{GPURenderPassColorAttachment/view}} must be [$valid to use with$] |device|.
        1. If |colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}} is [=map/exist|provided=]:

            1. |colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}} must be [$valid to use with$] |device|.

        1. |colorAttachment| must meet the [$GPURenderPassColorAttachment/GPURenderPassColorAttachment Valid Usage$] rules.

    1. If |this|.{{GPURenderPassDescriptor/depthStencilAttachment}} is [=map/exist|provided=]:

        1. |this|.{{GPURenderPassDescriptor/depthStencilAttachment}}.{{GPURenderPassDepthStencilAttachment/view}} must be [$valid to use with$] |device|.
        1. |this|.{{GPURenderPassDescriptor/depthStencilAttachment}} must meet the [$GPURenderPassDepthStencilAttachment/GPURenderPassDepthStencilAttachment Valid Usage$] rules.

    1. There must exist at least one attachment, either:
        - A non-`null` value in |this|.{{GPURenderPassDescriptor/colorAttachments}}, or
        - A |this|.{{GPURenderPassDescriptor/depthStencilAttachment}}.

    1. [$Validating GPURenderPassDescriptor's color attachment bytes per sample$](|device|, |this|.{{GPURenderPassDescriptor/colorAttachments}}) succeeds.

    1. All {{GPURenderPassColorAttachment/view}}s in non-`null` members of |this|.{{GPURenderPassDescriptor/colorAttachments}},
        and |this|.{{GPURenderPassDescriptor/depthStencilAttachment}}.{{GPURenderPassDepthStencilAttachment/view}}
        if present, must have equal {{GPUTexture/sampleCount}}s.

    1. For each {{GPURenderPassColorAttachment/view}} in non-`null` members of |this|.{{GPURenderPassDescriptor/colorAttachments}}
        and |this|.{{GPURenderPassDescriptor/depthStencilAttachment}}.{{GPURenderPassDepthStencilAttachment/view}},
        if present, the {{GPUTextureView/[[renderExtent]]}} must match.

    1. If |this|.{{GPURenderPassDescriptor/occlusionQuerySet}} is [=map/exists|provided=]:

        1. |this|.{{GPURenderPassDescriptor/occlusionQuerySet}} must be [$valid to use with$] |device|.
        1. |this|.{{GPURenderPassDescriptor/occlusionQuerySet}}.{{GPUQuerySet/type}}
            must be {{GPUQueryType/occlusion}}.

    1. If |this|.{{GPURenderPassDescriptor/timestampWrites}} is [=map/exist|provided=]:
        - [$Validate timestampWrites$](|device|, |this|.{{GPURenderPassDescriptor/timestampWrites}})
            must return true.
</div>

<div algorithm class=validusage data-timeline=device>
    <dfn abstract-op>Validating GPURenderPassDescriptor's color attachment bytes per sample</dfn>(|device|, |colorAttachments|)

    **Arguments:**

    - {{GPUDevice}} |device|
    - [=sequence=]&lt;{{GPURenderPassColorAttachment}}?&gt; |colorAttachments|

    [=Device timeline=] steps:

    1. Let |formats| be an empty [=list=]&lt;{{GPUTextureFormat}}?&gt;
    1. For each |colorAttachment| in |colorAttachments|:
        1. If |colorAttachment| is `undefined`, continue.
        1. [=list/Append=] |colorAttachment|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}} to |formats|.
    1. [$Calculating color attachment bytes per sample$](|formats|) must be &le; |device|.{{device/[[limits]]}}.{{supported limits/maxColorAttachmentBytesPerSample}}.
</div>

#### Color Attachments #### {#color-attachments}

<script type=idl>
dictionary GPURenderPassColorAttachment {
    required (GPUTexture or GPUTextureView) view;
    GPUIntegerCoordinate depthSlice;
    (GPUTexture or GPUTextureView) resolveTarget;

    GPUColor clearValue;
    required GPULoadOp loadOp;
    required GPUStoreOp storeOp;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderPassColorAttachment>
    : <dfn>view</dfn>
    ::
        Describes the texture [=subresource=] that will be output to for this color attachment.
        The [=subresource=] is determined by calling [$get as texture view$]({{GPURenderPassColorAttachment/view}}).

    : <dfn>depthSlice</dfn>
    ::
        Indicates the depth slice index of {{GPUTextureViewDimension/"3d"}} {{GPURenderPassColorAttachment/view}}
        that will be output to for this color attachment.

    : <dfn>resolveTarget</dfn>
    ::
        Describes the texture [=subresource=] that will receive the resolved output for this color
        attachment if {{GPURenderPassColorAttachment/view}} is multisampled.
        The [=subresource=] is determined by calling [$get as texture view$]({{GPURenderPassColorAttachment/resolveTarget}}).

    : <dfn>clearValue</dfn>
    ::
        Indicates the value to clear {{GPURenderPassColorAttachment/view}} to prior to executing the
        render pass. If not [=map/exist|provided=], defaults to `{r: 0, g: 0, b: 0, a: 0}`. Ignored
        if {{GPURenderPassColorAttachment/loadOp}} is not {{GPULoadOp/"clear"}}.

        The components of {{GPURenderPassColorAttachment/clearValue}} are all double values.
        They are converted [$to a texel value of texture format$] matching the render attachment.
        If conversion fails, a validation error is generated.

    : <dfn>loadOp</dfn>
    ::
        Indicates the load operation to perform on {{GPURenderPassColorAttachment/view}} prior to
        executing the render pass.

        Note: It is recommended to prefer clearing; see {{GPULoadOp/"clear"}} for details.

    : <dfn>storeOp</dfn>
    ::
        The store operation to perform on {{GPURenderPassColorAttachment/view}}
        after executing the render pass.
</dl>

<div algorithm dfn-for=GPURenderPassColorAttachment data-timeline=device>
    <dfn abstract-op>GPURenderPassColorAttachment Valid Usage</dfn>

    Given a {{GPURenderPassColorAttachment}} |this|:

    1. Let |renderViewDescriptor| be |this|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[descriptor]]}}.
    1. Let |renderTexture| be |this|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[texture]]}}.
    1. All of the requirements in the following steps |must| be met.
        <div class=validusage>
            1. |renderViewDescriptor|.{{GPUTextureViewDescriptor/format}} |must| be a [=color renderable format=].
            1. |this|.{{GPURenderPassColorAttachment/view}} |must| be a [$renderable texture view$].
            1. If |renderViewDescriptor|.{{GPUTextureViewDescriptor/dimension}} is {{GPUTextureViewDimension/"3d"}}:
                1. |this|.{{GPURenderPassColorAttachment/depthSlice}} |must| [=map/exist|be provided=] and |must|
                    be &lt; the [=GPUExtent3D/depthOrArrayLayers=] of the [=logical miplevel-specific texture extent=]
                    of the |renderTexture| [=subresource=] at [=mipmap level=] |renderViewDescriptor|.{{GPUTextureViewDescriptor/baseMipLevel}}.

                Otherwise:

                1. |this|.{{GPURenderPassColorAttachment/depthSlice}} |must| not [=map/exist|be provided=].

            1. If |this|.{{GPURenderPassColorAttachment/loadOp}} is {{GPULoadOp/"clear"}}:
                1. Converting the IDL value |this|.{{GPURenderPassColorAttachment/clearValue}}
                    [$to a texel value of texture format$] |renderViewDescriptor|.{{GPUTextureViewDescriptor/format}}
                    |must| not throw a {{TypeError}}.

                    Note: An error is not thrown if the value is out-of-range for the format but in-range for
                    the corresponding WGSL primitive type (`f32`, `i32`, or `u32`).
            1. If |this|.{{GPURenderPassColorAttachment/resolveTarget}} is [=map/exist|provided=]:
                1. Let |resolveViewDescriptor| be |this|.{{GPURenderPassColorAttachment/resolveTarget}}.{{GPUTextureView/[[descriptor]]}}.
                1. Let |resolveTexture| be |this|.{{GPURenderPassColorAttachment/resolveTarget}}.{{GPUTextureView/[[texture]]}}.
                1. |renderTexture|.{{GPUTexture/sampleCount}} |must| be &gt; 1.
                1. |resolveTexture|.{{GPUTexture/sampleCount}} |must| be 1.
                1. |this|.{{GPURenderPassColorAttachment/resolveTarget}} |must| be a non-3d [$renderable texture view$].
                1. |this|.{{GPURenderPassColorAttachment/resolveTarget}}.{{GPUTextureView/[[renderExtent]]}} and
                    |this|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[renderExtent]]}} |must| match.
                1. |resolveViewDescriptor|.{{GPUTextureViewDescriptor/format}} |must| equal
                    |renderViewDescriptor|.{{GPUTextureViewDescriptor/format}}.
                1. |resolveTexture|.{{GPUTextureDescriptor/format}} |must| equal
                    |renderTexture|.{{GPUTextureDescriptor/format}}.
                1. |resolveViewDescriptor|.{{GPUTextureViewDescriptor/format}} |must| support resolve according to [[#plain-color-formats]].
        </div>
</div>

<div algorithm data-timeline=device>
    A {{GPUTextureView}} |view| is a <dfn abstract-op>renderable texture view</dfn>
    if the all of the requirements in the following [=device timeline=] steps are met:

    <div class=validusage>
        1. Let |descriptor| be |view|.{{GPUTextureView/[[descriptor]]}}.
        1. |descriptor|.{{GPUTextureViewDescriptor/usage}}
            must contain {{GPUTextureUsage/RENDER_ATTACHMENT}}.
        1. |descriptor|.{{GPUTextureViewDescriptor/dimension}} must be {{GPUTextureViewDimension/"2d"}}
            or {{GPUTextureViewDimension/"2d-array"}} or {{GPUTextureViewDimension/"3d"}}.
        1. |descriptor|.{{GPUTextureViewDescriptor/mipLevelCount}} must be 1.
        1. |descriptor|.{{GPUTextureViewDescriptor/arrayLayerCount}} must be 1.
        1. |descriptor|.{{GPUTextureViewDescriptor/aspect}} must refer to all [=aspects=] of
            |view|.{{GPUTextureView/[[texture]]}}.
    </div>
</div>

<div algorithm data-timeline=const>
    <dfn abstract-op>Calculating color attachment bytes per sample</dfn>(|formats|)

    **Arguments:**

    - [=sequence=]&lt;{{GPUTextureFormat}}?&gt; |formats|

    **Returns:** {{GPUSize32}}

    1. Let |total| be 0.
    1. For each non-null |format| in |formats|
        1. [=Assert=]: |format| is a [=color renderable format=].
        1. Let |renderTargetPixelByteCost| be the [=render target pixel byte cost=] of |format|.
        1. Let |renderTargetComponentAlignment| be the [=render target component alignment=] of |format|.
        1. Round |total| up to the smallest multiple of |renderTargetComponentAlignment| greater than or equal to |total|.
        1. Add |renderTargetPixelByteCost| to |total|.
    1. Return |total|.

</div>

#### Depth/Stencil Attachments #### {#depth-stencil-attachments}

<script type=idl>
dictionary GPURenderPassDepthStencilAttachment {
    required (GPUTexture or GPUTextureView) view;

    float depthClearValue;
    GPULoadOp depthLoadOp;
    GPUStoreOp depthStoreOp;
    boolean depthReadOnly = false;

    GPUStencilValue stencilClearValue = 0;
    GPULoadOp stencilLoadOp;
    GPUStoreOp stencilStoreOp;
    boolean stencilReadOnly = false;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderPassDepthStencilAttachment>
    : <dfn>view</dfn>
    ::
        Describes the texture [=subresource=] that will be output to and read from for this
        depth/stencil attachment.
        The [=subresource=] is determined by calling [$get as texture view$]({{GPURenderPassDepthStencilAttachment/view}}).

    : <dfn>depthClearValue</dfn>
    ::
        Indicates the value to clear {{GPURenderPassDepthStencilAttachment/view}}'s depth component
        to prior to executing the render pass. Ignored if {{GPURenderPassDepthStencilAttachment/depthLoadOp}}
        is not {{GPULoadOp/"clear"}}. Must be between 0.0 and 1.0, inclusive.
        <!-- POSTV1(unrestricted-depth): unless unrestricted depth is enabled -->

    : <dfn>depthLoadOp</dfn>
    ::
        Indicates the load operation to perform on {{GPURenderPassDepthStencilAttachment/view}}'s
        depth component prior to executing the render pass.

        Note: It is recommended to prefer clearing; see {{GPULoadOp/"clear"}} for details.

    : <dfn>depthStoreOp</dfn>
    ::
        The store operation to perform on {{GPURenderPassDepthStencilAttachment/view}}'s
        depth component after executing the render pass.

    : <dfn>depthReadOnly</dfn>
    ::
        Indicates that the depth component of {{GPURenderPassDepthStencilAttachment/view}}
        is read only.

    : <dfn>stencilClearValue</dfn>
    ::
        Indicates the value to clear {{GPURenderPassDepthStencilAttachment/view}}'s stencil component
        to prior to executing the render pass. Ignored if {{GPURenderPassDepthStencilAttachment/stencilLoadOp}}
        is not {{GPULoadOp/"clear"}}.

        The value will be converted to the type of the stencil aspect of |view| by taking the same
        number of LSBs as the number of bits in the stencil aspect of one [=texel block|texel=] of |view|.

    : <dfn>stencilLoadOp</dfn>
    ::
        Indicates the load operation to perform on {{GPURenderPassDepthStencilAttachment/view}}'s
        stencil component prior to executing the render pass.

        Note: It is recommended to prefer clearing; see {{GPULoadOp/"clear"}} for details.

    : <dfn>stencilStoreOp</dfn>
    ::
        The store operation to perform on {{GPURenderPassDepthStencilAttachment/view}}'s
        stencil component after executing the render pass.

    : <dfn>stencilReadOnly</dfn>
    ::
        Indicates that the stencil component of {{GPURenderPassDepthStencilAttachment/view}}
        is read only.
</dl>

<div class=validusage dfn-for=GPURenderPassDepthStencilAttachment data-timeline=device>
    <dfn abstract-op>GPURenderPassDepthStencilAttachment Valid Usage</dfn>

    Given a {{GPURenderPassDepthStencilAttachment}} |this|, the following validation
    rules apply:

    - |this|.{{GPURenderPassDepthStencilAttachment/view}} must have a [=depth-or-stencil format=].
    - |this|.{{GPURenderPassDepthStencilAttachment/view}} must be a [$renderable texture view$].
    - Let |format| be |this|.{{GPURenderPassDepthStencilAttachment/view}}.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}}.
    - If |this|.{{GPURenderPassDepthStencilAttachment/depthLoadOp}} is {{GPULoadOp/"clear"}},
        |this|.{{GPURenderPassDepthStencilAttachment/depthClearValue}} must [=map/exist|be provided=] and must be between 0.0 and 1.0,
        inclusive.
        <!-- POSTV1(unrestricted-depth): unless unrestricted depth is enabled -->
    - If |format| has a depth aspect and |this|.{{GPURenderPassDepthStencilAttachment/depthReadOnly}} is `false`:
        - |this|.{{GPURenderPassDepthStencilAttachment/depthLoadOp}} must [=map/exist|be provided=].
        - |this|.{{GPURenderPassDepthStencilAttachment/depthStoreOp}} must [=map/exist|be provided=].

        Otherwise:

        - |this|.{{GPURenderPassDepthStencilAttachment/depthLoadOp}} must not [=map/exist|be provided=].
        - |this|.{{GPURenderPassDepthStencilAttachment/depthStoreOp}} must not [=map/exist|be provided=].
    - If |format| has a stencil aspect and |this|.{{GPURenderPassDepthStencilAttachment/stencilReadOnly}} is `false`:
        - |this|.{{GPURenderPassDepthStencilAttachment/stencilLoadOp}} must [=map/exist|be provided=].
        - |this|.{{GPURenderPassDepthStencilAttachment/stencilStoreOp}} must [=map/exist|be provided=].

        Otherwise:

        - |this|.{{GPURenderPassDepthStencilAttachment/stencilLoadOp}} must not [=map/exist|be provided=].
        - |this|.{{GPURenderPassDepthStencilAttachment/stencilStoreOp}} must not [=map/exist|be provided=].
</div>

#### Load &amp; Store Operations #### {#load-and-store-ops}

<script type=idl>
enum GPULoadOp {
    "load",
    "clear",
};
</script>

<dl dfn-type=enum-value dfn-for=GPULoadOp>
    : <dfn>"load"</dfn>
    ::
        Loads the existing value for this attachment into the render pass.

    : <dfn>"clear"</dfn>
    ::
        Loads a clear value for this attachment into the render pass.

        Note:
        On some GPU hardware (primarily mobile), {{GPULoadOp/"clear"}} is significantly cheaper
        because it avoids loading data from main memory into tile-local memory.
        On other GPU hardware, there isn't a significant difference. As a result, it is
        recommended to use {{GPULoadOp/"clear"}} rather than {{GPULoadOp/"load"}} in cases where the
        initial value doesn't matter (e.g. the render target will be cleared using a skybox).
</dl>

<script type=idl>
enum GPUStoreOp {
    "store",
    "discard",
};
</script>

<dl dfn-type=enum-value dfn-for=GPUStoreOp>
    : <dfn>"store"</dfn>
    ::
        Stores the resulting value of the render pass for this attachment.

    : <dfn>"discard"</dfn>
    ::
        Discards the resulting value of the render pass for this attachment.

        Note: Discarded attachments
        behave as if they are cleared to zero, but implementations are not required to perform a
        clear at the end of the render pass. Implementations which do not explicitly clear discarded
        attachments at the end of a pass must lazily clear them prior to the reading the attachment
        contents, which occurs via sampling, copies, attaching to a later render pass with
        {{GPULoadOp/"load"}}, displaying or reading back the canvas
        ([$get a copy of the image contents of a context$]), etc.
</dl>


#### Render Pass Layout #### {#render-pass-layout}

{{GPURenderPassLayout}} declares the layout of the render targets of a {{GPURenderBundle}}.
It is also used internally to describe
{{GPURenderPassEncoder}} [$derive render targets layout from pass|layouts$] and
{{GPURenderPipeline}} [$derive render targets layout from pipeline|layouts$].
It determines compatibility between render passes, render bundles, and render pipelines.

<script type=idl>
dictionary GPURenderPassLayout
         : GPUObjectDescriptorBase {
    required sequence<GPUTextureFormat?> colorFormats;
    GPUTextureFormat depthStencilFormat;
    GPUSize32 sampleCount = 1;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderPassLayout>
    : <dfn>colorFormats</dfn>
    ::
        A list of the {{GPUTextureFormat}}s of the color attachments for this pass or bundle.

    : <dfn>depthStencilFormat</dfn>
    ::
        The {{GPUTextureFormat}} of the depth/stencil attachment for this pass or bundle.

    : <dfn>sampleCount</dfn>
    ::
        Number of samples per pixel in the attachments for this pass or bundle.
</dl>

<div algorithm=gpurenderpasslayout-equal>
    Two {{GPURenderPassLayout}} values are <dfn dfn for="render pass layout" lt="equals|equal">equal</dfn> if:

    - Their {{GPURenderPassLayout/depthStencilFormat}} and {{GPURenderPassLayout/sampleCount}} are equal, and
    - Their {{GPURenderPassLayout/colorFormats}} are equal ignoring any trailing `null`s.
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>derive render targets layout from pass</dfn>

    **Arguments:**

    - {{GPURenderPassDescriptor}} |descriptor|

    **Returns:** {{GPURenderPassLayout}}

    [=Device timeline=] steps:

    1. Let |layout| be a new {{GPURenderPassLayout}} object.
    1. For each |colorAttachment| in |descriptor|.{{GPURenderPassDescriptor/colorAttachments}}:
        1. If |colorAttachment| is not `null`:
            1. Set |layout|.{{GPURenderPassLayout/sampleCount}} to |colorAttachment|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[texture]]}}.{{GPUTexture/sampleCount}}.
            1. Append |colorAttachment|.{{GPURenderPassColorAttachment/view}}.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}} to |layout|.{{GPURenderPassLayout/colorFormats}}.

            Otherwise:
            1. Append `null` to |layout|.{{GPURenderPassLayout/colorFormats}}.
    1. Let |depthStencilAttachment| be |descriptor|.{{GPURenderPassDescriptor/depthStencilAttachment}}.
    1. If |depthStencilAttachment| is not `null`:
        1. Let |view| be |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/view}}.
        1. Set |layout|.{{GPURenderPassLayout/sampleCount}} to |view|.{{GPUTextureView/[[texture]]}}.{{GPUTexture/sampleCount}}.
        1. Set |layout|.{{GPURenderPassLayout/depthStencilFormat}} to |view|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/format}}.
    1. Return |layout|.
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>derive render targets layout from pipeline</dfn>

    **Arguments:**

    - {{GPURenderPipelineDescriptor}} |descriptor|

    **Returns:** {{GPURenderPassLayout}}

    [=Device timeline=] steps:

    1. Let |layout| be a new {{GPURenderPassLayout}} object.
    1. Set |layout|.{{GPURenderPassLayout/sampleCount}} to |descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}}.
    1. If |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}} is [=map/exist|provided=]:
        1. Set |layout|.{{GPURenderPassLayout/depthStencilFormat}} to |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.{{GPUDepthStencilState/format}}.
    1. If |descriptor|.{{GPURenderPipelineDescriptor/fragment}} is [=map/exist|provided=]:
        1. For each |colorTarget| in |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}:
            1. Append |colorTarget|.{{GPUColorTargetState/format}} to |layout|.{{GPURenderPassLayout/colorFormats}}
                if |colorTarget| is not `null`, or append `null` otherwise.
    1. Return |layout|.
</div>

### Finalization ### {#render-pass-encoder-finalization}

The render pass encoder can be ended by calling {{GPURenderPassEncoder/end()}} once the user
has finished recording commands for the pass. Once {{GPURenderPassEncoder/end()}} has been
called the render pass encoder can no longer be used.

<dl dfn-type=method dfn-for=GPURenderPassEncoder>
    : <dfn>end()</dfn>
    ::
        Completes recording of the render pass commands sequence.

        <div algorithm=GPURenderPassEncoder.end>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Let |parentEncoder| be |this|.{{GPURenderPassEncoder/[[command_encoder]]}}.
                1. If any of the following requirements are unmet,
                    [$generate a validation error$] and return.

                    <div class=validusage>
                        - |this|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/open=]".
                        - |parentEncoder|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/locked=]".
                    </div>
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/ended=]".
                1. Set |parentEncoder|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/open=]".
                1. If any of the following requirements are unmet, [$invalidate$] |parentEncoder| and return.

                    <div class=validusage>
                        - |this| must be [$valid$].
                        - |this|.{{GPURenderCommandsMixin/[[usage scope]]}} must satisfy [=usage scope validation=].
                        - |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}} must [=list/is empty|be empty=].
                        - |this|.{{GPURenderPassEncoder/[[occlusion_query_active]]}} must be `false`.
                        - |this|.{{GPURenderCommandsMixin/[[drawCount]]}} must be &le; |this|.{{GPURenderPassEncoder/[[maxDrawCount]]}}.
                    </div>
                1. [=list/Extend=] |parentEncoder|.{{GPUCommandsMixin/[[commands]]}}
                    with |this|.{{GPUCommandsMixin/[[commands]]}}.
                1. If |this|.{{GPURenderPassEncoder/[[endTimestampWrite]]}} is not `null`:
                    1. [=list/Extend=] |parentEncoder|.{{GPUCommandsMixin/[[commands]]}}
                        with |this|.{{GPURenderPassEncoder/[[endTimestampWrite]]}}.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. For each non-`null` |colorAttachment| in |renderState|.{{RenderState/[[colorAttachments]]}}:
                    1. Let |colorView| be |colorAttachment|.{{GPURenderPassColorAttachment/view}}.
                    1. If |colorView|.{{GPUTextureView/[[descriptor]]}}.{{GPUTextureViewDescriptor/dimension}} is:
                        <dl class=switch>
                            : {{GPUTextureViewDimension/"3d"}}
                            ::
                                Let |colorSubregion| be |colorAttachment|.{{GPURenderPassColorAttachment/depthSlice}} of
                                |colorView|.

                            : Otherwise
                            ::
                                Let |colorSubregion| be |colorView|.
                        </dl>

                    1. If |colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}} is not `null`:
                        1. Resolve the multiple samples of every [=texel block|texel=] of |colorSubregion| to a single
                            sample and copy to |colorAttachment|.{{GPURenderPassColorAttachment/resolveTarget}}.

                    1. If |colorAttachment|.{{GPURenderPassColorAttachment/storeOp}} is:
                        <dl class=switch>
                            : {{GPUStoreOp/"store"}}
                            ::
                                Ensure the contents of the [=framebuffer memory=] associated with |colorSubregion|
                                are stored in |colorSubregion|.

                            : {{GPUStoreOp/"discard"}}
                            ::
                                Set every [=texel block|texel=] of |colorSubregion| to zero.
                        </dl>

                1. Let |depthStencilAttachment| be |renderState|.{{RenderState/[[depthStencilAttachment]]}}.
                1. If |depthStencilAttachment| is not `null`:
                    1. If |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthStoreOp}} is:
                        <dl class=switch>
                            : Not [=map/exist|provided=]
                            ::
                                [=Assert=] that |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/depthReadOnly}}
                                is `true` and leave the [=aspect/depth=] [=GPUTextureView/subresource=] of |depthStencilView|
                                unchanged.

                            : {{GPUStoreOp/"store"}}
                            ::
                                Ensure the contents of the [=framebuffer memory=] associated with the [=aspect/depth=]
                                [=GPUTextureView/subresource=] of |depthStencilView| are stored in |depthStencilView|.

                            : {{GPUStoreOp/"discard"}}
                            ::
                                Set every [=texel block|texel=] in the [=aspect/depth=] [=GPUTextureView/subresource=]
                                of |depthStencilView| to zero.
                        </dl>

                    1. If |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilStoreOp}} is:
                        <dl class=switch>
                            : Not [=map/exist|provided=]
                            ::
                                [=Assert=] that |depthStencilAttachment|.{{GPURenderPassDepthStencilAttachment/stencilReadOnly}}
                                is `true` and leave the [=aspect/stencil=] [=GPUTextureView/subresource=] of |depthStencilView|
                                unchanged.

                            : {{GPUStoreOp/"store"}}
                            ::
                                Ensure the contents of the [=framebuffer memory=] associated with the [=aspect/stencil=]
                                [=GPUTextureView/subresource=] of |depthStencilView| are stored in |depthStencilView|.

                            : {{GPUStoreOp/"discard"}}
                            ::
                                Set every [=texel block|texel=] in the [=aspect/stencil=] [=GPUTextureView/subresource=]
                                |depthStencilView| to zero.
                        </dl>

                1. Let |renderState| be `null`.

                Note: Discarded attachments behave as if they are cleared to zero, but implementations are not required
                to perform a clear at the end of the render pass. See the note on {{GPUStoreOp/"discard"}} for
                additional details.

                Note: [=Read-only depth-stencil=] attachments can be thought of as implicitly using the {{GPUStoreOp/"store"}}
                operation, but since their content is unchanged during the render pass implementations don't need to
                update the attachment. Validation that requires the store op to not be provided for read-only attachments
                is done in [$GPURenderPassDepthStencilAttachment/GPURenderPassDepthStencilAttachment Valid Usage$].
            </div>
        </div>
</dl>

<h3 id=gpurendercommandsmixin data-dfn-type=interface>`GPURenderCommandsMixin`
<span id=render-commands></span>
</h3>

{{GPURenderCommandsMixin}} defines rendering commands common to {{GPURenderPassEncoder}}
and {{GPURenderBundleEncoder}}.

<script type=idl>
interface mixin GPURenderCommandsMixin {
    undefined setPipeline(GPURenderPipeline pipeline);

    undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size);
    undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer? buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);

    undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
    undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1,
        optional GPUSize32 firstIndex = 0,
        optional GPUSignedOffset32 baseVertex = 0,
        optional GPUSize32 firstInstance = 0);

    undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
    undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};
</script>

{{GPURenderCommandsMixin}} assumes the presence of
{{GPUObjectBase}}, {{GPUCommandsMixin}}, and {{GPUBindingCommandsMixin}} members on the same object.
It must only be included by interfaces which also include those mixins.

{{GPURenderCommandsMixin}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPURenderCommandsMixin data-timeline=device>
    : <dfn>\[[layout]]</dfn>, of type {{GPURenderPassLayout}}, readonly
    ::
        The layout of the render pass.

    : <dfn>\[[depthReadOnly]]</dfn>, of type {{boolean}}, readonly
    ::
        If `true`, indicates that the depth component is not modified.

    : <dfn>\[[stencilReadOnly]]</dfn>, of type {{boolean}}, readonly
    ::
        If `true`, indicates that the stencil component is not modified.

    : <dfn>[[usage scope]]</dfn>, of type [=usage scope=], initially empty
    ::
        The [=usage scope=] for this render pass or bundle.

    : <dfn>\[[pipeline]]</dfn>, of type {{GPURenderPipeline}}, initially `null`
    ::
        The current {{GPURenderPipeline}}.

    : <dfn>\[[index_buffer]]</dfn>, of type {{GPUBuffer}}, initially `null`
    ::
        The current buffer to read index data from.

    : <dfn>\[[index_format]]</dfn>, of type {{GPUIndexFormat}}
    ::
        The format of the index data in {{GPURenderCommandsMixin/[[index_buffer]]}}.

    : <dfn>\[[index_buffer_offset]]</dfn>, of type {{GPUSize64}}
    ::
        The offset in bytes of the section of {{GPURenderCommandsMixin/[[index_buffer]]}} currently set.

    : <dfn>\[[index_buffer_size]]</dfn>, of type {{GPUSize64}}
    ::
        The size in bytes of the section of {{GPURenderCommandsMixin/[[index_buffer]]}} currently set,
        initially `0`.

    : <dfn>\[[vertex_buffers]]</dfn>, of type [=ordered map=]&lt;slot, {{GPUBuffer}}&gt;, initially empty
    ::
        The current {{GPUBuffer}}s to read vertex data from for each slot.

    : <dfn>\[[vertex_buffer_sizes]]</dfn>, of type [=ordered map=]&lt;slot, {{GPUSize64}}&gt;, initially empty
    ::
        The size in bytes of the section of {{GPUBuffer}} currently set for each slot.

    : <dfn>\[[drawCount]]</dfn>, of type {{GPUSize64}}
    ::
        The number of draw commands recorded in this encoder.
</dl>

<div algorithm data-timeline=device>
    To <dfn abstract-op>Enqueue a render command</dfn> on {{GPURenderCommandsMixin}} |encoder| which
    issues the steps of a [=GPU Command=] |command| with [=RenderState=] |renderState|, run the
    following [=device timeline=] steps:

        1. [=list/Append=] |command| to |encoder|.{{GPUCommandsMixin/[[commands]]}}.
        1. When |command| is executed as part of a {{GPUCommandBuffer}} |commandBuffer|:
            1. Issue the steps of |command| with |commandBuffer|.{{GPUCommandBuffer/[[renderState]]}} as |renderState|.
</div>

### Drawing ### {#render-pass-encoder-drawing}

<dl dfn-type=method dfn-for=GPURenderCommandsMixin>
    : <dfn>setPipeline(pipeline)</dfn>
    ::
        Sets the current {{GPURenderPipeline}}.

        <div algorithm=GPURenderCommandsMixin.setPipeline>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/setPipeline(pipeline)">
                    |pipeline|: The render pipeline to use for subsequent drawing commands.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |pipelineTargetsLayout| be [$derive render targets layout from pipeline$](|pipeline|.{{GPURenderPipeline/[[descriptor]]}}).
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |pipeline| is [$valid to use with$] |this|.
                        - |this|.{{GPURenderCommandsMixin/[[layout]]}} [=render pass layout/equals=] |pipelineTargetsLayout|.
                        - If |pipeline|.{{GPURenderPipeline/[[writesDepth]]}}:
                            |this|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} must be `false`.
                        - If |pipeline|.{{GPURenderPipeline/[[writesStencil]]}}:
                            |this|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} must be `false`.
                    </div>
                1. Set |this|.{{GPURenderCommandsMixin/[[pipeline]]}} to be |pipeline|.
            </div>
        </div>

    : <dfn>setIndexBuffer(buffer, indexFormat, offset, size)</dfn>
    ::
        Sets the current index buffer.

        <div algorithm=GPURenderCommandsMixin.setIndexBuffer>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/setIndexBuffer(buffer, indexFormat, offset, size)">
                    |buffer|: Buffer containing index data to use for subsequent drawing commands.
                    |indexFormat|: Format of the index data contained in |buffer|.
                    |offset|: Offset in bytes into |buffer| where the index data begins. Defaults to `0`.
                    |size|: Size in bytes of the index data in |buffer|.
                        Defaults to the size of the buffer minus the offset.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If |size| is missing, set |size| to max(0, |buffer|.{{GPUBuffer/size}} - |offset|).
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |buffer| is [$valid to use with$] |this|.
                        - |buffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/INDEX}}.
                        - |offset| is a multiple of |indexFormat|'s byte size.
                        - |offset| + |size| &le; |buffer|.{{GPUBuffer/size}}.
                    </div>
                1. [$usage scope/Add$] |buffer| to {{GPURenderCommandsMixin/[[usage scope]]}}
                    with usage [=internal usage/input=].
                1. Set |this|.{{GPURenderCommandsMixin/[[index_buffer]]}} to be |buffer|.
                1. Set |this|.{{GPURenderCommandsMixin/[[index_format]]}} to be |indexFormat|.
                1. Set |this|.{{GPURenderCommandsMixin/[[index_buffer_offset]]}} to be |offset|.
                1. Set |this|.{{GPURenderCommandsMixin/[[index_buffer_size]]}} to be |size|.
            </div>
        </div>

    : <dfn>setVertexBuffer(slot, buffer, offset, size)</dfn>
    ::
        Sets the current vertex buffer for the given slot.

        <div algorithm=GPURenderCommandsMixin.setVertexBuffer>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/setVertexBuffer(slot, buffer, offset, size)">
                    |slot|: The vertex buffer slot to set the vertex buffer for.
                    |buffer|: Buffer containing vertex data to use for subsequent drawing commands.
                    |offset|: Offset in bytes into |buffer| where the vertex data begins. Defaults to `0`.
                    |size|: Size in bytes of the vertex data in |buffer|.
                        Defaults to the size of the buffer minus the offset.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |bufferSize| be 0 if |buffer| is `null`, or |buffer|.{{GPUBuffer/size}} if not.
                1. If |size| is missing, set |size| to max(0, |bufferSize| - |offset|).
                1. If any of the following requirements are unmet, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |slot| must be &lt;
                            |this|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxVertexBuffers}}.
                        - |offset| must be a multiple of 4.
                        - |offset| + |size| must be &le; |bufferSize|.
                    </div>
                1. If |buffer| is `null`:
                    1. [=map/Remove=] |this|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}[|slot|].
                    1. [=map/Remove=] |this|.{{GPURenderCommandsMixin/[[vertex_buffer_sizes]]}}[|slot|].

                    Otherwise:

                    1. If any of the following requirements are unmet, [$invalidate$] |this| and return.

                        <div class=validusage>
                            - |buffer| must be [$valid to use with$] |this|.
                            - |buffer|.{{GPUBuffer/usage}} must contain {{GPUBufferUsage/VERTEX}}.
                        </div>
                    1. [$usage scope/Add$] |buffer| to {{GPURenderCommandsMixin/[[usage scope]]}}
                        with usage [=internal usage/input=].
                    1. Set |this|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}[|slot|] to be |buffer|.
                    1. Set |this|.{{GPURenderCommandsMixin/[[vertex_buffer_sizes]]}}[|slot|] to be |size|.
            </div>
        </div>

    : <dfn>draw(vertexCount, instanceCount, firstVertex, firstInstance)</dfn>
    ::
        Draws primitives.
        See [[#rendering-operations]] for the detailed specification.

        <div algorithm=GPURenderCommandsMixin.draw>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/draw(vertexCount, instanceCount, firstVertex, firstInstance)">
                    |vertexCount|: The number of vertices to draw.
                    |instanceCount|: The number of instances to draw.
                    |firstVertex|: Offset into the vertex buffers, in vertices, to begin drawing from.
                    |firstInstance|: First instance to draw.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. All of the requirements in the following steps |must| be met.
                    If any are unmet, [$invalidate$] |this| and return.

                    <div class=validusage>
                        1. It |must| be [$valid to draw$] with |this|.
                        1. Let |buffers| be |this|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}}.
                        1. For each {{GPUIndex32}} |slot| from `0` to |buffers|.[=list/size=] (non-inclusive):
                            1. If |buffers|[|slot|] is `null`, [=iteration/continue=].
                            1. Let |bufferSize| be |this|.{{GPURenderCommandsMixin/[[vertex_buffer_sizes]]}}[|slot|].
                            1. Let |stride| be |buffers|[|slot|].{{GPUVertexBufferLayout/arrayStride}}.
                            1. Let |attributes| be |buffers|[|slot|].{{GPUVertexBufferLayout/attributes}}
                            1. Let |lastStride| be the maximum value of
                                (|attribute|.{{GPUVertexAttribute/offset}} &plus; [$GPUVertexFormat/byteSize$](|attribute|.{{GPUVertexAttribute/format}}))
                                over each |attribute| in |attributes|, or 0 if |attributes| is [=list/empty=].
                            1. Let |strideCount| be computed based on |buffers|[|slot|].{{GPUVertexBufferLayout/stepMode}}:

                                <dl class=switch>
                                    : {{GPUVertexStepMode/"vertex"}}
                                    :: |firstVertex| &plus; |vertexCount|
                                    : {{GPUVertexStepMode/"instance"}}
                                    :: |firstInstance| &plus; |instanceCount|
                                </dl>
                            1. If |strideCount| &ne; `0`:
                                1. (|strideCount| &minus; `1`) &times; |stride| &plus; |lastStride| |must| be &le; |bufferSize|.
                    </div>
                1. Increment |this|.{{GPURenderCommandsMixin/[[drawCount]]}} by 1.

                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Draw |instanceCount| instances, starting with instance |firstInstance|, of
                    primitives consisting of |vertexCount| vertices, starting with vertex |firstVertex|,
                    with the states from |bindingState| and |renderState|.
            </div>
        </div>

    : <dfn>drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)</dfn>
    ::
        Draws indexed primitives.
        See [[#rendering-operations]] for the detailed specification.

        <div algorithm=GPURenderCommandsMixin.drawIndexed>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance)">
                    |indexCount|: The number of indices to draw.
                    |instanceCount|: The number of instances to draw.
                    |firstIndex|: Offset into the index buffer, in indices, begin drawing from.
                    |baseVertex|: Added to each index value before indexing into the vertex buffers.
                    |firstInstance|: First instance to draw.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - It is [$valid to draw indexed$] with |this|.
                        - |firstIndex| + |indexCount| &le; |this|.{{GPURenderCommandsMixin/[[index_buffer_size]]}}
                            &div; |this|.{{GPURenderCommandsMixin/[[index_format]]}}'s byte size;
                        - Let |buffers| be |this|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}}.
                        - For each {{GPUIndex32}} |slot| from `0` to |buffers|.[=list/size=] (non-inclusive):
                            - If |buffers|[|slot|] is `null`, [=iteration/continue=].
                            - Let |bufferSize| be |this|.{{GPURenderCommandsMixin/[[vertex_buffer_sizes]]}}[|slot|].
                            - Let |stride| be |buffers|[|slot|].{{GPUVertexBufferLayout/arrayStride}}.
                            - Let |lastStride| be max(|attribute|.{{GPUVertexAttribute/offset}} &plus; [$GPUVertexFormat/byteSize$](|attribute|.{{GPUVertexAttribute/format}}))
                                for each |attribute| in |buffers|[|slot|].{{GPUVertexBufferLayout/attributes}}.
                            - Let |strideCount| be |firstInstance| &plus; |instanceCount|.
                            - If |buffers|[|slot|].{{GPUVertexBufferLayout/stepMode}} is {{GPUVertexStepMode/"instance"}} and |strideCount| &ne; `0`:
                                - Ensure (|strideCount| &minus; `1`) &times; |stride| &plus; |lastStride| &le; |bufferSize|.
                    </div>
                1. Increment |this|.{{GPURenderCommandsMixin/[[drawCount]]}} by 1.

                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Draw |instanceCount| instances, starting with instance |firstInstance|, of
                    primitives consisting of |indexCount| indexed vertices, starting with index
                    |firstIndex| from vertex |baseVertex|,
                    with the states from |bindingState| and |renderState|.
            </div>

            Note:
            WebGPU applications should never use index data with indices out of bounds of any
            bound vertex buffer that has {{GPUVertexStepMode}} {{GPUVertexStepMode/"vertex"}}.
            WebGPU implementations have different ways of handling this,
            and therefore a range of behaviors is allowed.
            Either the whole draw call is discarded, or the access to those attributes
            out of bounds is described by WGSL's [=invalid memory reference=].
        </div>

    : <dfn>drawIndirect(indirectBuffer, indirectOffset)</dfn>
    ::
        Draws primitives using parameters read from a {{GPUBuffer}}.
        See [[#rendering-operations]] for the detailed specification.

        The <dfn dfn for="">indirect draw parameters</dfn> encoded in the buffer must be a tightly
        packed block of **four 32-bit unsigned integer values (16 bytes total)**, given in the same
        order as the arguments for {{GPURenderCommandsMixin/draw()}}. For example:

        <pre highlight=js>
            let drawIndirectParameters = new Uint32Array(4);
            drawIndirectParameters[0] = vertexCount;
            drawIndirectParameters[1] = instanceCount;
            drawIndirectParameters[2] = firstVertex;
            drawIndirectParameters[3] = firstInstance;
        </pre>

        The value corresponding to `firstInstance` must be 0, unless the {{GPUFeatureName/"indirect-first-instance"}}
        [=feature=] is enabled.  If the {{GPUFeatureName/"indirect-first-instance"}} [=feature=] is not enabled and
        `firstInstance` is not zero the {{GPURenderCommandsMixin/drawIndirect()}} call will be treated as a no-op.

        <div algorithm=GPURenderCommandsMixin.drawIndirect>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/drawIndirect(indirectBuffer, indirectOffset)">
                    |indirectBuffer|: Buffer containing the [=indirect draw parameters=].
                    |indirectOffset|: Offset in bytes into |indirectBuffer| where the drawing data begins.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - It is [$valid to draw$] with |this|.
                        - |indirectBuffer| is [$valid to use with$] |this|.
                        - |indirectBuffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/INDIRECT}}.
                        - |indirectOffset| + sizeof([=indirect draw parameters=]) &le;
                            |indirectBuffer|.{{GPUBuffer/size}}.
                        - |indirectOffset| is a multiple of 4.
                    </div>
                1. [$usage scope/Add$] |indirectBuffer| to {{GPURenderCommandsMixin/[[usage scope]]}}
                    with usage [=internal usage/input=].
                1. Increment |this|.{{GPURenderCommandsMixin/[[drawCount]]}} by 1.

                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |vertexCount| be an unsigned 32-bit integer read from |indirectBuffer| at
                    |indirectOffset| bytes.
                1. Let |instanceCount| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 4) bytes.
                1. Let |firstVertex| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 8) bytes.
                1. Let |firstInstance| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 12) bytes.
                1. Draw |instanceCount| instances, starting with instance |firstInstance|, of
                    primitives consisting of |vertexCount| vertices, starting with vertex |firstVertex|,
                    with the states from |bindingState| and |renderState|.
            </div>
        </div>

    : <dfn>drawIndexedIndirect(indirectBuffer, indirectOffset)</dfn>
    ::
        Draws indexed primitives using parameters read from a {{GPUBuffer}}.
        See [[#rendering-operations]] for the detailed specification.

        The <dfn dfn for="">indirect drawIndexed parameters</dfn> encoded in the buffer must be a
        tightly packed block of **five 32-bit values (20 bytes total)**, given in the same order as
        the arguments for {{GPURenderCommandsMixin/drawIndexed()}}. The value corresponding to
        `baseVertex` is a signed 32-bit integer, and all others are unsigned 32-bit integers.
        For example:

        <pre highlight=js>
            let drawIndexedIndirectParameters = new Uint32Array(5);
            let drawIndexedIndirectParametersSigned = new Int32Array(drawIndexedIndirectParameters.buffer);
            drawIndexedIndirectParameters[0] = indexCount;
            drawIndexedIndirectParameters[1] = instanceCount;
            drawIndexedIndirectParameters[2] = firstIndex;
            // baseVertex is a signed value.
            drawIndexedIndirectParametersSigned[3] = baseVertex;
            drawIndexedIndirectParameters[4] = firstInstance;
        </pre>

        The value corresponding to `firstInstance` must be 0, unless the {{GPUFeatureName/"indirect-first-instance"}}
        [=feature=] is enabled.  If the {{GPUFeatureName/"indirect-first-instance"}} [=feature=] is not enabled and
        `firstInstance` is not zero the {{GPURenderCommandsMixin/drawIndexedIndirect()}} call will be treated as a no-op.

        <div algorithm=GPURenderCommandsMixin.drawIndexedIndirect>
            <div data-timeline=content>
                **Called on:** {{GPURenderCommandsMixin}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderCommandsMixin/drawIndexedIndirect(indirectBuffer, indirectOffset)">
                    |indirectBuffer|: Buffer containing the [=indirect drawIndexed parameters=].
                    |indirectOffset|: Offset in bytes into |indirectBuffer| where the drawing data begins.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - It is [$valid to draw indexed$] with |this|.
                        - |indirectBuffer| is [$valid to use with$] |this|.
                        - |indirectBuffer|.{{GPUBuffer/usage}} contains {{GPUBufferUsage/INDIRECT}}.
                        - |indirectOffset| + sizeof([=indirect drawIndexed parameters=]) &le;
                            |indirectBuffer|.{{GPUBuffer/size}}.
                        - |indirectOffset| is a multiple of 4.
                    </div>
                1. [$usage scope/Add$] |indirectBuffer| to {{GPURenderCommandsMixin/[[usage scope]]}}
                    with usage [=internal usage/input=].
                1. Increment |this|.{{GPURenderCommandsMixin/[[drawCount]]}} by 1.

                1. Let |bindingState| be a snapshot of |this|'s current state.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |indexCount| be an unsigned 32-bit integer read from |indirectBuffer| at
                    |indirectOffset| bytes.
                1. Let |instanceCount| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 4) bytes.
                1. Let |firstIndex| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 8) bytes.
                1. Let |baseVertex| be a signed 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 12) bytes.
                1. Let |firstInstance| be an unsigned 32-bit integer read from |indirectBuffer| at
                    (|indirectOffset| + 16) bytes.
                1. Draw |instanceCount| instances, starting with instance |firstInstance|, of
                    primitives consisting of |indexCount| indexed vertices, starting with index
                    |firstIndex| from vertex |baseVertex|,
                    with the states from |bindingState| and |renderState|.
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    To determine if it's <dfn abstract-op>valid to draw</dfn> with {{GPURenderCommandsMixin}} |encoder|,
    run the following [=device timeline=] steps:

    1. If any of the following conditions are unsatisfied, return `false`:

        <div class=validusage>
            - [$Validate encoder bind groups$](|encoder|, |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}})
                must be `true`.
            - Let |pipelineDescriptor| be |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}.
            - For each {{GPUIndex32}} |slot| `0` to
                |pipelineDescriptor|.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}}.[=list/size=]:
                - If |pipelineDescriptor|.{{GPURenderPipelineDescriptor/vertex}}.{{GPUVertexState/buffers}}[|slot|] is not `null`,
                    |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}} must [=map/contain=] |slot|.
            - Validate {{supported limits/maxBindGroupsPlusVertexBuffers}}:
                1. Let |bindGroupSpaceUsed| be
                    (the maximum key in |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}) + 1.
                1. Let |vertexBufferSpaceUsed| be
                    (the maximum key in |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}) + 1.
                1. |bindGroupSpaceUsed| + |vertexBufferSpaceUsed| must be &le;
                    |encoder|.{{GPUObjectBase/[[device]]}}.{{device/[[limits]]}}.{{supported limits/maxBindGroupsPlusVertexBuffers}}.
        </div>

        Otherwise, return `true`.
</div>

<div algorithm data-timeline=device>
    To determine if it's <dfn abstract-op>valid to draw indexed</dfn> with {{GPURenderCommandsMixin}} |encoder|,
    run the following [=device timeline=] steps:

    1. If any of the following conditions are unsatisfied, return `false`:

        <div class=validusage>
            - It must be [$valid to draw$] with |encoder|.
            - |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} must not be `null`.
            - Let |topology| be |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/topology}}.
            - If |topology| is {{GPUPrimitiveTopology/"line-strip"}} or {{GPUPrimitiveTopology/"triangle-strip"}}:
                - |encoder|.{{GPURenderCommandsMixin/[[index_format]]}} must equal
                    |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}}.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/stripIndexFormat}}.
        </div>

        Otherwise, return `true`.
</div>

### Rasterization state ### {#render-pass-encoder-rasterization-state}

The {{GPURenderPassEncoder}} has several methods which affect how draw commands are rasterized to
attachments used by this encoder.

<dl dfn-type=method dfn-for=GPURenderPassEncoder>
    : <dfn>setViewport(x, y, width, height, minDepth, maxDepth)</dfn>
    ::
        Sets the viewport used during the rasterization stage to linearly map from
          [=NDC|normalized device coordinates=] to [=viewport coordinates=].

        <div algorithm=GPURenderPassEncoder.setViewport>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/setViewport(x, y, width, height, minDepth, maxDepth)">
                    |x|: Minimum X value of the viewport in pixels.
                    |y|: Minimum Y value of the viewport in pixels.
                    |width|: Width of the viewport in pixels.
                    |height|: Height of the viewport in pixels.
                    |minDepth|: Minimum depth value of the viewport.
                    |maxDepth|: Maximum depth value of the viewport.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. Let |maxViewportRange| be |this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension2D}} &times; `2`.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |x| &ge; -|maxViewportRange|
                        - |y| &ge; -|maxViewportRange|
                        - `0` &le; |width| &le; |this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension2D}}
                        - `0` &le; |height| &le; |this|.{{GPUDevice/limits}}.{{GPUSupportedLimits/maxTextureDimension2D}}
                        - |x| + |width| &le; |maxViewportRange| &minus; `1`
                        - |y| + |height| &le; |maxViewportRange| &minus; `1`
                        - `0.0` &le; |minDepth| &le; `1.0`
                        - `0.0` &le; |maxDepth| &le; `1.0`
                        - |minDepth| &le; |maxDepth|
                    </div>

                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Round |x|, |y|, |width|, and |height| to some uniform precision, no less precise than integer rounding.
                1. Set |renderState|.{{RenderState/[[viewport]]}} to the extents |x|, |y|, |width|, |height|, |minDepth|, and |maxDepth|.
            </div>
        </div>

    : <dfn>setScissorRect(x, y, width, height)</dfn>
    ::
        Sets the scissor rectangle used during the rasterization stage.
        After transformation into [=viewport coordinates=] any fragments which fall outside the scissor
        rectangle will be discarded.

        <div algorithm=GPURenderPassEncoder.setScissorRect>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/setScissorRect(x, y, width, height)">
                    |x|: Minimum X value of the scissor rectangle in pixels.
                    |y|: Minimum Y value of the scissor rectangle in pixels.
                    |width|: Width of the scissor rectangle in pixels.
                    |height|: Height of the scissor rectangle in pixels.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |x|+|width| &le;
                            |this|.{{GPURenderPassEncoder/[[attachment_size]]}}.width.
                        - |y|+|height| &le;
                            |this|.{{GPURenderPassEncoder/[[attachment_size]]}}.height.
                    </div>

                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Set |renderState|.{{RenderState/[[scissorRect]]}} to the extents |x|, |y|, |width|, and |height|.
            </div>
        </div>

    : <dfn>setBlendConstant(color)</dfn>
    ::
        Sets the constant blend color and alpha values used with {{GPUBlendFactor/"constant"}}
        and {{GPUBlendFactor/"one-minus-constant"}} {{GPUBlendFactor}}s.

        <div algorithm=GPURenderPassEncoder.setBlendConstant>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/setBlendConstant(color)">
                    |color|: The color to use when blending.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUColor shape$](|color|).
                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Set |renderState|.{{RenderState/[[blendConstant]]}} to |color|.
            </div>
        </div>

    : <dfn>setStencilReference(reference)</dfn>
    ::
        Sets the {{RenderState/[[stencilReference]]}} value used during stencil tests with
        the {{GPUStencilOperation/"replace"}} {{GPUStencilOperation}}.

        <div algorithm=GPURenderPassEncoder.setStencilReference>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/setStencilReference(reference)">
                    |reference|: The new stencil reference value.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Set |renderState|.{{RenderState/[[stencilReference]]}} to |reference|.
            </div>
        </div>
</dl>

### Queries ### {#render-pass-encoder-queries}

<dl dfn-type=method dfn-for=GPURenderPassEncoder>
    : <dfn>beginOcclusionQuery(queryIndex)</dfn>
    ::
        <div algorithm=GPURenderPassEncoder.beginOcclusionQuery>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/beginOcclusionQuery(queryIndex)">
                    |queryIndex|: The index of the query in the query set.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |this|.{{GPURenderPassEncoder/[[occlusion_query_set]]}} is not `null`.
                        - |queryIndex| &lt; |this|.{{GPURenderPassEncoder/[[occlusion_query_set]]}}.{{GPUQuerySet/count}}.
                        - The query at same |queryIndex| must not have been previously written to in this pass.
                        - |this|.{{GPURenderPassEncoder/[[occlusion_query_active]]}} is `false`.
                    </div>
                1. Set |this|.{{GPURenderPassEncoder/[[occlusion_query_active]]}} to `true`.

                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Set |renderState|.{{RenderState/[[occlusionQueryIndex]]}} to |queryIndex|.
            </div>
        </div>

    : <dfn>endOcclusionQuery()</dfn>
    ::
        <div algorithm=GPURenderPassEncoder.endOcclusionQuery>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} this.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - |this|.{{GPURenderPassEncoder/[[occlusion_query_active]]}} is `true`.
                    </div>
                1. Set |this|.{{GPURenderPassEncoder/[[occlusion_query_active]]}} to `false`.

                1. [$Enqueue a render command$] on |this| which issues the subsequent steps on the
                    [=Queue timeline=] with |renderState| when executed.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |passingFragments| be non-zero if any fragment samples passed all per-fragment
                    tests since the corresponding {{GPURenderPassEncoder/beginOcclusionQuery()}}
                    command was executed, and zero otherwise.

                    Note: If no draw calls occurred, |passingFragments| is zero.
                1. Write |passingFragments| into
                    |this|.{{GPURenderPassEncoder/[[occlusion_query_set]]}} at index
                    |renderState|.{{RenderState/[[occlusionQueryIndex]]}}.
            </div>
        </div>
</dl>

### Bundles ### {#render-pass-encoder-bundles}

<dl dfn-type=method dfn-for=GPURenderPassEncoder>
    : <dfn>executeBundles(bundles)</dfn>
    ::
        Executes the commands previously recorded into the given {{GPURenderBundle}}s as part of
        this render pass.

        When a {{GPURenderBundle}} is executed, it does not inherit the render pass's pipeline, bind
        groups, or vertex and index buffers. After a {{GPURenderBundle}} has executed, the render
        pass's pipeline, bind group, and vertex/index buffer state is cleared
        (to the initial, empty values).

        Note: The state is cleared, not restored to the previous state.
        This occurs even if zero {{GPURenderBundle|GPURenderBundles}} are executed.

        <div algorithm=GPURenderPassEncoder.executeBundles>
            <div data-timeline=content>
                **Called on:** {{GPURenderPassEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderPassEncoder/executeBundles(bundles)">
                    |bundles|: List of render bundles to execute.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. [$Validate the encoder state$] of |this|. If it returns false, return.
                1. If any of the following conditions are unsatisfied, [$invalidate$] |this| and return.

                    <div class=validusage>
                        - For each |bundle| in |bundles|:
                            - |bundle| must be [$valid to use with$] |this|.
                            - |this|.{{GPURenderCommandsMixin/[[layout]]}} must equal |bundle|.{{GPURenderBundle/[[layout]]}}.
                            - If |this|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} is true, |bundle|.{{GPURenderBundle/[[depthReadOnly]]}} must be true.
                            - If |this|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} is true, |bundle|.{{GPURenderBundle/[[stencilReadOnly]]}} must be true.
                    </div>

                1. For each |bundle| in |bundles|:
                    1. Increment |this|.{{GPURenderCommandsMixin/[[drawCount]]}} by |bundle|.{{GPURenderBundle/[[drawCount]]}}.
                    1. [$usage scope/Merge$] |bundle|.{{GPURenderCommandsMixin/[[usage scope]]}} into
                        |this|.{{GPURenderCommandsMixin/[[usage scope]]}}.
                    1. [$Enqueue a render command$] on |this| which issues the following steps on the
                        [=Queue timeline=] with |renderState| when executed:

                        <div data-timeline=queue>
                            [=Queue timeline=] steps:

                            1. Execute each command in |bundle|.{{GPURenderBundle/[[command_list]]}}
                                with |renderState|.

                                Note: |renderState| cannot be changed by executing render bundles. Binding state was
                                already captured at bundle encoding time, and so isn't used when executing bundles.
                        </div>

                1. [$Reset the render pass binding state$] of |this|.
            </div>
        </div>
</dl>

<div algorithm data-timeline=device>
    To <dfn abstract-op>Reset the render pass binding state</dfn> of {{GPURenderPassEncoder}} |encoder| run
    the following [=device timeline=] steps:

        1. [=map/Clear=] |encoder|.{{GPUBindingCommandsMixin/[[bind_groups]]}}.
        1. Set |encoder|.{{GPURenderCommandsMixin/[[pipeline]]}} to `null`.
        1. Set |encoder|.{{GPURenderCommandsMixin/[[index_buffer]]}} to `null`.
        1. [=map/Clear=] |encoder|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}.
</div>

# Bundles # {#bundles}

A bundle is a partial, limited pass that is encoded once and can then be executed multiple times as
part of future pass encoders without expiring after use like typical command buffers. This can
reduce the overhead of encoding and submission of commands which are issued repeatedly without
changing.

<h3 id=gpurenderbundle data-dfn-type=interface>`GPURenderBundle`
<span id=render-bundle></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPURenderBundle {
};
GPURenderBundle includes GPUObjectBase;
</script>

<dl dfn-type=attribute dfn-for=GPURenderBundle>
    : <dfn>\[[command_list]]</dfn>, of type [=list=]&lt;[=GPU command=]&gt;
    ::
        A [=list=] of [=GPU commands=] to be submitted to the {{GPURenderPassEncoder}} when the
        {{GPURenderBundle}} is executed.

    : <dfn>[[usage scope]]</dfn>, of type [=usage scope=], initially empty
    ::
        The [=usage scope=] for this render bundle, stored for later merging into the
        {{GPURenderPassEncoder}}'s {{GPURenderCommandsMixin/[[usage scope]]}}
        in {{GPURenderPassEncoder/executeBundles()}}.

    : <dfn>\[[layout]]</dfn>, of type {{GPURenderPassLayout}}
    ::
        The layout of the render bundle.

    : <dfn>\[[depthReadOnly]]</dfn>, of type {{boolean}}
    ::
        If `true`, indicates that the depth component is not modified by executing this render bundle.

    : <dfn>\[[stencilReadOnly]]</dfn>, of type {{boolean}}
    ::
        If `true`, indicates that the stencil component is not modified by executing this render bundle.

    : <dfn>\[[drawCount]]</dfn>, of type {{GPUSize64}}
    ::
        The number of draw commands in this {{GPURenderBundle}}.
</dl>

### Render Bundle Creation ### {#render-bundle-creation}

<script type=idl>
dictionary GPURenderBundleDescriptor
         : GPUObjectDescriptorBase {
};
</script>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPURenderBundleEncoder {
    GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
};
GPURenderBundleEncoder includes GPUObjectBase;
GPURenderBundleEncoder includes GPUCommandsMixin;
GPURenderBundleEncoder includes GPUDebugCommandsMixin;
GPURenderBundleEncoder includes GPUBindingCommandsMixin;
GPURenderBundleEncoder includes GPURenderCommandsMixin;
</script>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createRenderBundleEncoder(descriptor)</dfn>
    ::
        Creates a {{GPURenderBundleEncoder}}.

        <div algorithm=GPUDevice.createRenderBundleEncoder>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createRenderBundleEncoder(descriptor)">
                    |descriptor|: Description of the {{GPURenderBundleEncoder}} to create.
                </pre>

                **Returns:** {{GPURenderBundleEncoder}}

                [=Content timeline=] steps:

                1. [=?=] [$Validate texture format required features$] of each non-`null` element of
                    |descriptor|.{{GPURenderPassLayout/colorFormats}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. If |descriptor|.{{GPURenderPassLayout/depthStencilFormat}} is [=map/exists|provided=]:
                    1. [=?=] [$Validate texture format required features$] of
                        |descriptor|.{{GPURenderPassLayout/depthStencilFormat}} with |this|.{{GPUObjectBase/[[device]]}}.
                1. Let |e| be [=!=] [$create a new WebGPU object$](|this|, {{GPURenderBundleEncoder}}, |descriptor|).
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |e|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following conditions are unsatisfied
                    [$generate a validation error$], [$invalidate$] |e| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - |descriptor|.{{GPURenderPassLayout/colorFormats}}.[=list/size=] must be &le;
                            |this|.{{device/[[limits]]}}.{{supported limits/maxColorAttachments}}.
                        - For each non-`null` |colorFormat| in |descriptor|.{{GPURenderPassLayout/colorFormats}}:
                            - |colorFormat| must be a [=color renderable format=].
                        - [$Calculating color attachment bytes per sample$](|descriptor|.{{GPURenderPassLayout/colorFormats}})
                            must be &le; |this|.{{device/[[limits]]}}.{{supported limits/maxColorAttachmentBytesPerSample}}.
                        - If |descriptor|.{{GPURenderPassLayout/depthStencilFormat}} is [=map/exist|provided=]:
                            - |descriptor|.{{GPURenderPassLayout/depthStencilFormat}} must be a
                                [=depth-or-stencil format=].
                        - There must exist at least one attachment, either:
                            - A non-`null` value in
                                |descriptor|.{{GPURenderPassLayout/colorFormats}}, or
                            - A |descriptor|.{{GPURenderPassLayout/depthStencilFormat}}.
                    </div>
                1. Set |e|.{{GPURenderCommandsMixin/[[layout]]}} to a copy of |descriptor|'s included {{GPURenderPassLayout}} interface.
                1. Set |e|.{{GPURenderCommandsMixin/[[depthReadOnly]]}} to |descriptor|.{{GPURenderBundleEncoderDescriptor/depthReadOnly}}.
                1. Set |e|.{{GPURenderCommandsMixin/[[stencilReadOnly]]}} to |descriptor|.{{GPURenderBundleEncoderDescriptor/stencilReadOnly}}.
                1. Set |e|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/open=]".
                1. Set |e|.{{GPURenderCommandsMixin/[[drawCount]]}} to 0.
            </div>
        </div>
</dl>

### Encoding ### {#render-bundle-encoding}

<script type=idl>
dictionary GPURenderBundleEncoderDescriptor
         : GPURenderPassLayout {
    boolean depthReadOnly = false;
    boolean stencilReadOnly = false;
};
</script>

<dl dfn-type=dict-member dfn-for=GPURenderBundleEncoderDescriptor>
    : <dfn>depthReadOnly</dfn>
    ::
        If `true`, indicates that the render bundle does not modify the depth component of the
        {{GPURenderPassDepthStencilAttachment}} of any render pass the render bundle is executed
        in.

        See [=read-only depth-stencil=].

    : <dfn>stencilReadOnly</dfn>
    ::
        If `true`, indicates that the render bundle does not modify the stencil component of the
        {{GPURenderPassDepthStencilAttachment}} of any render pass the render bundle is executed
        in.

        See [=read-only depth-stencil=].
</dl>

### Finalization ### {#render-bundle-finalization}

<dl dfn-type=method dfn-for=GPURenderBundleEncoder>
    : <dfn>finish(descriptor)</dfn>
    ::
        Completes recording of the render bundle commands sequence.

        <div algorithm=GPURenderBundleEncoder.finish>
            <div data-timeline=content>
                **Called on:** {{GPURenderBundleEncoder}} this.

                **Arguments:**

                <pre class=argumentdef for="GPURenderBundleEncoder/finish(descriptor)">
                    descriptor:
                </pre>

                **Returns:** {{GPURenderBundle}}

                [=Content timeline=] steps:

                1. Let |renderBundle| be a new {{GPURenderBundle}}.
                1. Issue the |finish steps| on the [=Device timeline=] of
                    |this|.{{GPUObjectBase/[[device]]}}.
                1. Return |renderBundle|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |finish steps|:

                1. Let |validationSucceeded| be `true` if all of the following requirements are met, and `false` otherwise.

                    <div class=validusage>
                        - |this| must be [$valid$].
                        - |this|.{{GPURenderCommandsMixin/[[usage scope]]}} must satisfy [=usage scope validation=].
                        - |this|.{{GPUCommandsMixin/[[state]]}} must be "[=encoder state/open=]".
                        - |this|.{{GPUDebugCommandsMixin/[[debug_group_stack]]}} must [=list/is empty|be empty=].
                    </div>
                1. Set |this|.{{GPUCommandsMixin/[[state]]}} to "[=encoder state/ended=]".
                1. If |validationSucceeded| is `false`, then:
                    1. [$Generate a validation error$].
                    1. Return an [$invalidated$] {{GPURenderBundle}}.
                1. Set |renderBundle|.{{GPURenderBundle/[[command_list]]}} to
                    |this|.{{GPUCommandsMixin/[[commands]]}}.
                1. Set |renderBundle|.{{GPURenderBundle/[[usage scope]]}} to
                    |this|.{{GPURenderCommandsMixin/[[usage scope]]}}.
                1. Set |renderBundle|.{{GPURenderBundle/[[drawCount]]}} to
                    |this|.{{GPURenderCommandsMixin/[[drawCount]]}}.
            </div>
        </div>
</dl>

# Queues # {#queues}

<h3 id=gpuqueuedescriptor data-dfn-type=dictionary>`GPUQueueDescriptor`
<span id=dictdef-gpuqueuedescriptor></span>
</h3>

{{GPUQueueDescriptor}} describes a queue request.

<script type=idl>
dictionary GPUQueueDescriptor
         : GPUObjectDescriptorBase {
};
</script>

<h3 id=gpuqueue data-dfn-type=interface>`GPUQueue`
<span id=gpu-queue></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUQueue {
    undefined submit(sequence<GPUCommandBuffer> commandBuffers);

    Promise<undefined> onSubmittedWorkDone();

    undefined writeBuffer(
        GPUBuffer buffer,
        GPUSize64 bufferOffset,
        AllowSharedBufferSource data,
        optional GPUSize64 dataOffset = 0,
        optional GPUSize64 size);

    undefined writeTexture(
        GPUTexelCopyTextureInfo destination,
        AllowSharedBufferSource data,
        GPUTexelCopyBufferLayout dataLayout,
        GPUExtent3D size);

    undefined copyExternalImageToTexture(
        GPUCopyExternalImageSourceInfo source,
        GPUCopyExternalImageDestInfo destination,
        GPUExtent3D copySize);
};
GPUQueue includes GPUObjectBase;
</script>

{{GPUQueue}} has the following methods:

<dl dfn-type=method dfn-for=GPUQueue>
    : <dfn>writeBuffer(buffer, bufferOffset, data, dataOffset, size)</dfn>
    ::
        Issues a write operation of the provided data into a {{GPUBuffer}}.

        <div algorithm=GPUQueue.writeBuffer>
            <div data-timeline=content>
                **Called on:** {{GPUQueue}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUQueue/writeBuffer(buffer, bufferOffset, data, dataOffset, size)">
                    |buffer|: The buffer to write to.
                    |bufferOffset|: Offset in bytes into |buffer| to begin writing at.
                    |data|: Data to write into |buffer|.
                    |dataOffset|: Offset in into |data| to begin writing from. Given in elements if
                        |data| is a `TypedArray` and bytes otherwise.
                    |size|: Size of content to write from |data| to |buffer|. Given in elements if
                        |data| is a `TypedArray` and bytes otherwise.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. If |data| is an {{ArrayBuffer}} or {{DataView}}, let the element type be "byte".
                    Otherwise, |data| is a TypedArray; let the element type be the type of the TypedArray.
                1. Let |dataSize| be the size of |data|, in elements.
                1. If |size| is missing,
                    let |contentsSize| be |dataSize| &minus; |dataOffset|.
                    Otherwise, let |contentsSize| be |size|.
                1. If any of the following conditions are unsatisfied,
                    throw an {{OperationError}} and return.

                    <div class=validusage>
                        - |contentsSize| &ge; 0.
                        - |dataOffset| + |contentsSize| &le; |dataSize|.
                        - |contentsSize|, converted to bytes, is a multiple of 4 bytes.
                    </div>
                1. Let |dataContents| be [=get a copy of the buffer source|a copy of the bytes held by the buffer source=] |data|.
                1. Let |contents| be the |contentsSize| elements of |dataContents| starting at
                    an offset of |dataOffset| elements.
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If any of the following conditions are unsatisfied,
                    [$generate a validation error$] and return.

                    <div class=validusage>
                        - |buffer| is [$valid to use with$] |this|.
                        - |buffer|.{{GPUBuffer/[[internal state]]}} is "[=GPUBuffer/[[internal state]]/available=]".
                        - |buffer|.{{GPUBuffer/usage}} includes {{GPUBufferUsage/COPY_DST}}.
                        - |bufferOffset|, converted to bytes, is a multiple of 4 bytes.
                        - |bufferOffset| + |contentsSize|, converted to bytes, &le; |buffer|.{{GPUBuffer/size}} bytes.
                    </div>
                1. Issue the subsequent steps on the [=Queue timeline=] of |this|.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Write |contents| into |buffer| starting at |bufferOffset|.
            </div>
        </div>

    : <dfn>writeTexture(destination, data, dataLayout, size)</dfn>
    ::
        Issues a write operation of the provided data into a {{GPUTexture}}.

        <div algorithm=GPUQueue.writeTexture>
            <div data-timeline=content>
                **Called on:** {{GPUQueue}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUQueue/writeTexture(destination, data, dataLayout, size)">
                    |destination|: The [=texture subresource=] and origin to write to.
                    |data|: Data to write into |destination|.
                    |dataLayout|: Layout of the content in |data|.
                    |size|: Extents of the content to write from |data| to |destination|.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUOrigin3D shape$](|destination|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUExtent3D shape$](|size|).
                1. Let |dataBytes| be [=get a copy of the buffer source|a copy of the bytes held by the buffer source=] |data|.

                    Note: This is described as copying all of |data| to the device timeline,
                    but in practice |data| could be much larger than necessary.
                    Implementations should optimize by copying only the necessary bytes.
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Let |aligned| be `false`.
                1. Let |dataLength| be |dataBytes|.[=byte sequence/length=].
                1. If any of the following conditions are unsatisfied,
                    [$generate a validation error$] and return.

                    <div class=validusage>
                        - |destination|.{{GPUTexelCopyTextureInfo/texture}}.{{GPUTexture/[[destroyed]]}} is `false`.
                        - [$validating texture buffer copy$](|destination|, |dataLayout|, |dataLength|, |size|, {{GPUTextureUsage/COPY_DST}}, |aligned|) returns `true`.

                        Note: unlike
                        {{GPUCommandEncoder}}.{{GPUCommandEncoder/copyBufferToTexture()}},
                        there is no alignment requirement on either
                        |dataLayout|.{{GPUTexelCopyBufferLayout/bytesPerRow}} or |dataLayout|.{{GPUTexelCopyBufferLayout/offset}}.
                    </div>

                1. Issue the subsequent steps on the [=Queue timeline=] of |this|.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. Let |blockWidth| be the [=texel block width=] of |destination|.{{GPUTexelCopyTextureInfo/texture}}.
                1. Let |blockHeight| be the [=texel block height=] of |destination|.{{GPUTexelCopyTextureInfo/texture}}.

                1. Let |dstOrigin| be |destination|.{{GPUTexelCopyTextureInfo/origin}};
                1. Let |dstBlockOriginX| be (|dstOrigin|.[=GPUOrigin3D/x=] &div; |blockWidth|).
                1. Let |dstBlockOriginY| be (|dstOrigin|.[=GPUOrigin3D/y=] &div; |blockHeight|).

                1. Let |blockColumns| be (|copySize|.[=GPUExtent3D/width=] &div; |blockWidth|).
                1. Let |blockRows| be (|copySize|.[=GPUExtent3D/height=] &div; |blockHeight|).

                1. [=Assert=] that |dstBlockOriginX|, |dstBlockOriginY|, |blockColumns|, and |blockRows| are integers.

                1. For each |z| in the range [0, |copySize|.[=GPUExtent3D/depthOrArrayLayers=] &minus; 1]:
                    1. Let |dstSubregion| be [$texture copy sub-region$] (|z| &plus; |dstOrigin|.[=GPUOrigin3D/z=]) of |destination|.

                    1. For each |y| in the range [0, |blockRows| &minus; 1]:
                        1. For each |x| in the range [0, |blockColumns| &minus; 1]:
                            1. Let |blockOffset| be the [$texel block byte offset$] of |dataLayout| for (|x|, |y|, |z|) of
                                |destination|.{{GPUTexelCopyTextureInfo/texture}}.

                            1. Set [=texel block=] (|dstBlockOriginX| &plus; |x|, |dstBlockOriginY| &plus; |y|) of
                                |dstSubregion| to be an [=equivalent texel representation=] to the [=texel block=]
                                described by |dataBytes| at offset |blockOffset|.
            </div>
        </div>

    : <dfn>copyExternalImageToTexture(source, destination, copySize)</dfn>
    ::
        Issues a copy operation of the contents of a platform image/canvas
        into the destination texture.

        This operation performs [[#color-space-conversions|color encoding]] into the destination
        encoding according to the parameters of {{GPUCopyExternalImageDestInfo}}.

        Copying into a `-srgb` texture results in the same texture bytes, not the same decoded
        values, as copying into the corresponding non-`-srgb` format.
        Thus, after a copy operation, sampling the destination texture has
        different results depending on whether its format is `-srgb`, all else unchanged.

        <!-- POSTV1(srgb-linear): If added, explain here how it interacts. -->

        <div class=note heading>
            When copying from a `"webgl"`/`"webgl2"` context canvas, the
            [=WebGL Drawing Buffer=] may be not exist during certain points in the
            frame presentation cycle (after the image has been moved to the compositor
            for display). To avoid this, either:

            - Issue {{GPUQueue/copyExternalImageToTexture()}} in the same [=task=] with
                WebGL rendering operation, to ensure the copy occurs before the WebGL
                canvas is presented.
            - If not possible, set the `preserveDrawingBuffer` option in
                {{WebGLContextAttributes}} to `true`, so that the drawing buffer will
                still contain a copy of the frame contents after they've been presented.
                Note, this extra copy may have a performance cost.
        </div>

        <div algorithm=GPUQueue.copyExternalImageToTexture>
            <div data-timeline=content>
                **Called on:** {{GPUQueue}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUQueue/copyExternalImageToTexture(source, destination, copySize)">
                    |source|: source image and origin to copy to |destination|.
                    |destination|: The [=texture subresource=] and origin to write to, and its encoding metadata.
                    |copySize|: Extents of the content to write from |source| to |destination|.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. [=?=] [$validate GPUOrigin2D shape$](|source|.{{GPUCopyExternalImageSourceInfo/origin}}).
                1. [=?=] [$validate GPUOrigin3D shape$](|destination|.{{GPUTexelCopyTextureInfo/origin}}).
                1. [=?=] [$validate GPUExtent3D shape$](|copySize|).
                1. Let |sourceImage| be |source|.{{GPUCopyExternalImageSourceInfo/source}}
                1. If |sourceImage| <l spec=html>[=is not origin-clean=]</l>,
                    throw a {{SecurityError}} and return.
                1. If any of the following requirements are unmet, throw an {{OperationError}} and return.

                    <div class=validusage>
                        - |source|.|origin|.[=GPUOrigin2D/x=] + |copySize|.[=GPUExtent3D/width=]
                            must be &le; the width of |sourceImage|.
                        - |source|.|origin|.[=GPUOrigin2D/y=] + |copySize|.[=GPUExtent3D/height=]
                            must be &le; the height of |sourceImage|.
                        - |copySize|.[=GPUExtent3D/depthOrArrayLayers=]
                            must be &le; 1.
                    </div>
                1. Let |usability| be [=?=] [=check the usability of the image argument=](|source|).
                1. Issue the subsequent steps on the [=Device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Let |texture| be |destination|.{{GPUTexelCopyTextureInfo/texture}}.
                1. If any of the following requirements are unmet, [$generate a validation error$] and return.

                    <div class=validusage>
                        - |usability| must be `good`.
                        - |texture|.{{GPUTexture/[[destroyed]]}} must be `false`.
                        - |texture| must be [$valid to use with$] |this|.
                        - [$validating GPUTexelCopyTextureInfo$](destination, copySize) must return `true`.
                        - |texture|.{{GPUTexture/usage}} must include both
                            {{GPUTextureUsage/RENDER_ATTACHMENT}} and {{GPUTextureUsage/COPY_DST}}.
                        - |texture|.{{GPUTexture/dimension}} must be {{GPUTextureDimension/"2d"}}.
                        - |texture|.{{GPUTexture/sampleCount}} must be 1.
                        - |texture|.{{GPUTexture/format}} must be one of the following
                            formats (which all support {{GPUTextureUsage/RENDER_ATTACHMENT}} usage):
                            - {{GPUTextureFormat/"r8unorm"}}
                            - {{GPUTextureFormat/"r16float"}}
                            - {{GPUTextureFormat/"r32float"}}
                            - {{GPUTextureFormat/"rg8unorm"}}
                            - {{GPUTextureFormat/"rg16float"}}
                            - {{GPUTextureFormat/"rg32float"}}
                            - {{GPUTextureFormat/"rgba8unorm"}}
                            - {{GPUTextureFormat/"rgba8unorm-srgb"}}
                            - {{GPUTextureFormat/"bgra8unorm"}}
                            - {{GPUTextureFormat/"bgra8unorm-srgb"}}
                            - {{GPUTextureFormat/"rgb10a2unorm"}}
                            - {{GPUTextureFormat/"rgba16float"}}
                            - {{GPUTextureFormat/"rgba32float"}}
                    </div>

                1. If |copySize|.[=GPUExtent3D/depthOrArrayLayers=] is &gt; 0, issue the subsequent
                    steps on the [=Queue timeline=] of |this|.
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. [=Assert=] that the [=texel block width=] of |destination|.{{GPUTexelCopyTextureInfo/texture}} is 1,
                    the [=texel block height=] of |destination|.{{GPUTexelCopyTextureInfo/texture}} is 1, and that
                    |copySize|.[=GPUExtent3D/depthOrArrayLayers=] is 1.

                1. Let |srcOrigin| be |source|.{{GPUCopyExternalImageSourceInfo/origin}}.
                1. Let |dstOrigin| be |destination|.{{GPUTexelCopyTextureInfo/origin}}.
                1. Let |dstSubregion| be [$texture copy sub-region$] (|dstOrigin|.[=GPUOrigin3D/z=]) of |destination|.

                1. For each |y| in the range [0, |copySize|.[=GPUExtent3D/height=] &minus; 1]:
                    1. Let |srcY| be |y| if |source|.{{GPUCopyExternalImageSourceInfo/flipY}} is `false` and
                        (|copySize|.[=GPUExtent3D/height=] &minus; 1 &minus; |y|) otherwise.
                    1. For each |x| in the range [0, |copySize|.[=GPUExtent3D/width=] &minus; 1]:
                        1. Set [=texel block=]
                            (|dstOrigin|.[=GPUOrigin3D/x=] &plus; |x|, |dstOrigin|.[=GPUOrigin3D/y=] &plus; |y|) of
                            |dstSubregion| to be an [=equivalent texel representation=] of the pixel at
                            (|srcOrigin|.[=GPUOrigin2D/x=] &plus; |x|, |srcOrigin|.[=GPUOrigin2D/y=] &plus; |srcY|) of
                            |source|.{{GPUCopyExternalImageSourceInfo/source}} after applying any
                            [[#color-space-conversions|color encoding]] required by
                            |destination|.{{GPUCopyExternalImageDestInfo/colorSpace}} and
                            |destination|.{{GPUCopyExternalImageDestInfo/premultipliedAlpha}}.
            </div>
        </div>

    : <dfn>submit(commandBuffers)</dfn>
    ::
        Schedules the execution of the command buffers by the GPU on this queue.

        Submitted command buffers cannot be used again.

        <div algorithm=GPUQueue.submit>
            <div data-timeline=content>
                **Called on:** {{GPUQueue}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUQueue/submit(commandBuffers)">
                    |commandBuffers|:
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|:
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If any of the following requirements are unmet, [$generate a validation error$],
                    [$invalidate$] each {{GPUCommandBuffer}} in |commandBuffers| and return.

                    <div class=validusage>
                        - Every {{GPUCommandBuffer}} in |commandBuffers| must be [$valid to use with$] |this|.
                        - Every {{GPUCommandBuffer}} in |commandBuffers| must be unique.
                        - For each of the following types of resources used by any command in any
                            element of |commandBuffers|:

                            <dl class=switch>
                                : {{GPUBuffer}} |b|
                                :: |b|.{{GPUBuffer/[[internal state]]}} must
                                    be "[=GPUBuffer/[[internal state]]/available=]".
                                : {{GPUTexture}} |t|
                                :: |t|.{{GPUTexture/[[destroyed]]}} must be `false`.
                                : {{GPUExternalTexture}} |et|
                                :: |et|.{{GPUExternalTexture/[[expired]]}} must be `false`.
                                : {{GPUQuerySet}} |qs|
                                :: |qs|.{{GPUQuerySet/[[destroyed]]}} must be `false`.
                            </dl>

                            Note:
                            For occlusion queries, the {{GPURenderPassDescriptor/occlusionQuerySet}}
                            in {{GPUCommandEncoder/beginRenderPass()}} is not "used" unless
                            it is also used by {{GPURenderPassEncoder/beginOcclusionQuery()}}.
                    </div>

                1. For each |commandBuffer| in |commandBuffers|:
                    1. [$Invalidate$] |commandBuffer|.

                1. Issue the subsequent steps on the [=Queue timeline=] of |this|:
            </div>
            <div data-timeline=queue>
                [=Queue timeline=] steps:

                1. For each |commandBuffer| in |commandBuffers|:
                    1. Execute each command in |commandBuffer|.{{GPUCommandBuffer/[[command_list]]}}.
            </div>
        </div>

    : <dfn>onSubmittedWorkDone()</dfn>
    ::
        Returns a {{Promise}} that resolves once this queue finishes processing all the work submitted
        up to this moment.

        Resolution of this {{Promise}} implies the completion of
        {{GPUBuffer/mapAsync()}} calls made prior to that call,
        on {{GPUBuffer}}s last used exclusively on that queue.

        <div algorithm=GPUQueue.onSubmittedWorkDone>
            <div data-timeline=content>
                **Called on:** {{GPUQueue}} |this|.

                **Returns:** {{Promise}}&lt;{{undefined}}&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |synchronization steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |synchronization steps|:

                1. Let |event| occur upon the completion of
                    <span data-timeline=queue>all currently-enqueued operations</span>.
                1. [$Listen for timeline event$] |event|
                    on |this|.{{GPUObjectBase/[[device]]}}, handled by
                    the subsequent steps on <var data-timeline=content>contentTimeline</var>.
            </div>
            <div data-timeline=content>
                [=Content timeline=] steps:

                1. [=Resolve=] |promise|.
            </div>
        </div>
</dl>


# Queries # {#queries}

<h3 id=gpuqueryset data-dfn-type=interface>`GPUQuerySet`
<span id=queryset></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUQuerySet {
    undefined destroy();

    readonly attribute GPUQueryType type;
    readonly attribute GPUSize32Out count;
};
GPUQuerySet includes GPUObjectBase;
</script>

{{GPUQuerySet}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUQuerySet data-timeline=const>
    : <dfn>type</dfn>
    ::
        The type of the queries managed by this {{GPUQuerySet}}.

    : <dfn>count</dfn>
    ::
        The number of queries managed by this {{GPUQuerySet}}.
</dl>

{{GPUQuerySet}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUQuerySet data-timeline=device>
    : <dfn>\[[destroyed]]</dfn>, of type {{boolean}}, initially `false`
    ::
        If the query set is destroyed, it can no longer be used in any operation,
        and its underlying memory can be freed.
</dl>

### QuerySet Creation ### {#queryset-creation}

A {{GPUQuerySetDescriptor}} specifies the options to use in creating a {{GPUQuerySet}}.

<script type=idl>
dictionary GPUQuerySetDescriptor
         : GPUObjectDescriptorBase {
    required GPUQueryType type;
    required GPUSize32 count;
};
</script>

<dl dfn-type=dict-member dfn-for=GPUQuerySetDescriptor>
    : <dfn>type</dfn>
    ::
        The type of queries managed by {{GPUQuerySet}}.

    : <dfn>count</dfn>
    ::
        The number of queries managed by {{GPUQuerySet}}.
</dl>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>createQuerySet(descriptor)</dfn>
    ::
        Creates a {{GPUQuerySet}}.

        <div algorithm=GPUDevice.createQuerySet>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} this.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/createQuerySet(descriptor)">
                    descriptor: Description of the {{GPUQuerySet}} to create.
                </pre>

                **Returns:** {{GPUQuerySet}}

                [=Content timeline=] steps:

                1. If |descriptor|.{{GPUQuerySetDescriptor/type}} is {{GPUQueryType/"timestamp"}},
                    but {{GPUFeatureName/"timestamp-query"}} is not [=enabled for=] |this|:
                    1. Throw a {{TypeError}}.
                1. Let |q| be [=!=] [$create a new WebGPU object$](|this|, {{GPUQuerySet}}, |descriptor|).
                1. Set |q|.{{GPUQuerySet/type}} to |descriptor|.{{GPUQuerySetDescriptor/type}}.
                1. Set |q|.{{GPUQuerySet/count}} to |descriptor|.{{GPUQuerySetDescriptor/count}}.
                1. Issue the |initialization steps| on the [=Device timeline=] of |this|.
                1. Return |q|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |initialization steps|:

                1. If any of the following requirements are unmet, [$generate a validation error$],
                    [$invalidate$] |q| and return.

                    <div class=validusage>
                        - |this| must not be [$invalid|lost$].
                        - |descriptor|.{{GPUQuerySetDescriptor/count}} must be &le; 4096.
                    </div>

                1. Create a device allocation for |q| where each entry in the query set is zero.

                    If the allocation fails without side-effects,
                    [$generate an out-of-memory error$], [$invalidate$] |q|, and return.
            </div>
        </div>
</dl>

<div class=example>
    Creating a {{GPUQuerySet}} which holds 32 occlusion query results.

    <pre highlight=js>
        const querySet = gpuDevice.createQuerySet({
            type: 'occlusion',
            count: 32
        });
    </pre>
</div>

### Query Set Destruction ### {#queryset-destruction}

An application that no longer requires a {{GPUQuerySet}} can choose to lose access to it before
garbage collection by calling {{GPUQuerySet/destroy()}}.

{{GPUQuerySet}} has the following methods:

<dl dfn-type=method dfn-for=GPUQuerySet>
    : <dfn>destroy()</dfn>
    ::
        Destroys the {{GPUQuerySet}}.

        <div algorithm=GPUQuerySet.destroy>
            <div data-timeline=content>
                **Called on:** {{GPUQuerySet}} |this|.

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=device timeline=].
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Set |this|.{{GPUQuerySet/[[destroyed]]}} to `true`.
            </div>
        </div>
</dl>

## QueryType ## {#querytype}

<script type=idl>
enum GPUQueryType {
    "occlusion",
    "timestamp",
};
</script>

## Occlusion Query ## {#occlusion}

Occlusion query is only available on render passes, to query the number of fragment samples that pass
all the per-fragment tests for a set of drawing commands, including scissor, sample mask, alpha to
coverage, stencil, and depth tests. Any non-zero result value for the query indicates that at least
one sample passed the tests and reached the output merging stage of the render pipeline, 0 indicates
that no samples passed the tests.

When beginning a render pass, {{GPURenderPassDescriptor}}.{{GPURenderPassDescriptor/occlusionQuerySet}}
must be set to be able to use occlusion queries during the pass. An occlusion query is begun
and ended by calling {{GPURenderPassEncoder/beginOcclusionQuery()}} and
{{GPURenderPassEncoder/endOcclusionQuery()}} in pairs that cannot be nested, and resolved into a
{{GPUBuffer}} as a [=64-bit unsigned integer=] by {{GPUCommandEncoder}}.{{GPUCommandEncoder/resolveQuerySet()}}.

## Timestamp Query ## {#timestamp}

Timestamp queries allow applications to write timestamps to a {{GPUQuerySet}}, using:

- {{GPUComputePassDescriptor}}.{{GPUComputePassDescriptor/timestampWrites}}
- {{GPURenderPassDescriptor}}.{{GPURenderPassDescriptor/timestampWrites}}

and then resolve timestamp values (in nanoseconds as a [=64-bit unsigned integer=]) into
a {{GPUBuffer}}, using {{GPUCommandEncoder}}.{{GPUCommandEncoder/resolveQuerySet()}}.

Timestamp values are [=implementation-defined=] and may not increase monotonically. The physical device
may reset the timestamp counter occasionally, which can result in unexpected values such as negative
deltas between timestamps that logically should be monotonically increasing. These instances should
be rare and can safely be ignored. Applications should not be written in such a way that unexpected
timestamps cause an application failure.

<p tracking-vector>
Timestamp queries are implemented using high-resolution timers (see [[#security-timing-device]]).
To mitigate security and privacy concerns, their precision must be reduced:

<div algorithm data-timeline=queue>
    To get the <dfn abstract-op>current queue timestamp</dfn>, run the following [=queue timeline=] steps:

    - Let |fineTimestamp| be the current timestamp value of the current [=queue timeline=],
        in nanoseconds, relative to an [=implementation-defined=] point in the past.
    - Return the result of calling [=coarsen time=] on |fineTimestamp|
        with `crossOriginIsolatedCapability` set to `false`.

    Note: Since cross-origin isolation may not apply to the [=device timeline=] or
    [=queue timeline=], `crossOriginIsolatedCapability` is never set to `true`.
</div>

<div algorithm data-timeline=device>
    <dfn abstract-op>Validate timestampWrites</dfn>(|device|, |timestampWrites|)

    **Arguments:**

    - {{GPUDevice}} |device|
    - <code>({{GPUComputePassTimestampWrites}} or {{GPURenderPassTimestampWrites}})</code> |timestampWrites|

    [=Device timeline=] steps:

    1. Return `true` if the following requirements are met, and `false` if not:

        <div class=validusage>
            - {{GPUFeatureName/"timestamp-query"}} must be [=enabled for=] |device|.
            - |timestampWrites|.`querySet` must be [$valid to use with$] |device|.
            - |timestampWrites|.`querySet`.{{GPUQuerySet/type}} must be {{GPUQueryType/"timestamp"}}.
            - Of the write index members in |timestampWrites| (`beginningOfPassWriteIndex`, `endOfPassWriteIndex`):
                - At least one must be [=map/exist|provided=].
                - Of those which are [=map/exist|provided=]:
                    - No two may be equal.
                    - Each must be &lt; |timestampWrites|.`querySet`.{{GPUQuerySet/count}}.
        </div>

    <!-- editorial note: any additional timestamp write locations that are compute- or
    render-specific could either be written here conditionally, or written at the call sites
    in compute/render pass descriptor validation. -->
</div>

# Canvas Rendering # {#canvas-rendering}

## {{HTMLCanvasElement/getContext()|HTMLCanvasElement.getContext()}} ## {#canvas-getcontext}

A {{GPUCanvasContext}} object is [$create a 'webgpu' context on a canvas|created$]
via the {{HTMLCanvasElement/getContext()}} method of an {{HTMLCanvasElement}}
instance by passing the string literal `'webgpu'` as its `contextType` argument.

<div class=example>
    Get a {{GPUCanvasContext}} from an offscreen {{HTMLCanvasElement}}:

    <pre highlight=js>
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('webgpu');
    </pre>
</div>

Unlike WebGL or 2D context creation, the second argument of
{{HTMLCanvasElement/getContext()|HTMLCanvasElement.getContext()}} or
{{OffscreenCanvas/getContext()|OffscreenCanvas.getContext()}},
the context creation attribute dictionary `options`, is ignored.
Instead, use {{GPUCanvasContext/configure()|GPUCanvasContext.configure()}},
which allows changing the canvas configuration without replacing the canvas.

<div algorithm data-timeline=content>
    To <dfn abstract-op>create a 'webgpu' context on a canvas</dfn>
    ({{HTMLCanvasElement}} or {{OffscreenCanvas}}) |canvas|, run the following
    [=content timeline=] steps:

    1. Let |context| be a new {{GPUCanvasContext}}.
    1. Set |context|.{{GPUCanvasContext/canvas}} to |canvas|.
    1. [$Replace the drawing buffer$] of |context|.
    1. Return |context|.

    Note: User agents should consider issuing developer-visible warnings when
    an ignored `options` argument is provided when calling `getContext()`
    to get a WebGPU canvas context.
</div>

## GPUCanvasContext ## {#canvas-context}

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUCanvasContext {
    readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;

    undefined configure(GPUCanvasConfiguration configuration);
    undefined unconfigure();

    GPUCanvasConfiguration? getConfiguration();
    GPUTexture getCurrentTexture();
};
</script>

{{GPUCanvasContext}} has the following [=content timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUCanvasContext data-timeline=content>
    : <dfn>canvas</dfn>
    ::
        The canvas this context was created from.

    : <dfn>\[[configuration]]</dfn>, of type {{GPUCanvasConfiguration}}?, initially `null`
    ::
        The options this context is currently configured with.

        `null` if the context has not been configured or has been
        {{GPUCanvasContext/unconfigure()|unconfigured}}.

    : <dfn>\[[textureDescriptor]]</dfn>, of type {{GPUTextureDescriptor}}?, initially `null`
    ::
        The currently configured texture descriptor, derived from the
        {{GPUCanvasContext/[[configuration]]}} and canvas.

        `null` if the context has not been configured or has been
        {{GPUCanvasContext/unconfigure()|unconfigured}}.

    : <dfn>\[[drawingBuffer]]</dfn>, an image, initially
        a transparent black image with the same size as the canvas
    ::
        The drawing buffer is the working-copy image data of the canvas.
        It is exposed as writable by {{GPUCanvasContext/[[currentTexture]]}}
        (returned by {{GPUCanvasContext/getCurrentTexture()}}).

        The drawing buffer is used to [$get a copy of the image contents of a context$], which
        occurs when the canvas is displayed or otherwise read. It may be transparent, even if
        {{GPUCanvasContext/[[configuration]]}}.{{GPUCanvasConfiguration/alphaMode}} is
        {{GPUCanvasAlphaMode/"opaque"}}. The {{GPUCanvasConfiguration/alphaMode}} only affects the
        result of the "[$get a copy of the image contents of a context$]" algorithm.

        The drawing buffer outlives the {{GPUCanvasContext/[[currentTexture]]}} and contains the
        previously-rendered contents even after the canvas has been presented.
        It is only cleared in [$Replace the drawing buffer$].

        Any time the drawing buffer is read, implementations must ensure that all previously
        submitted work (e.g. queue submissions) have completed writing to it via
        {{GPUCanvasContext/[[currentTexture]]}}.

    : <dfn>\[[currentTexture]]</dfn>, of type {{GPUTexture}}?, initially `null`
    ::
        The {{GPUTexture}} to draw into for the current frame.
        It exposes a writable view onto the underlying {{GPUCanvasContext/[[drawingBuffer]]}}.
        {{GPUCanvasContext/getCurrentTexture()}} populates this slot if `null`, then returns it.

        In the steady-state of a visible canvas, any changes to the drawing buffer made through the
        currentTexture get presented when [$updating the rendering of a WebGPU canvas$].
        At or before that point, the texture is also destroyed
        and {{GPUCanvasContext/[[currentTexture]]}} is set to to `null`, signalling that
        a new one is to be created by the next call to {{GPUCanvasContext/getCurrentTexture()}}.

        {{GPUTexture/destroy()|Destroying}} the currentTexture has no effect on the drawing buffer
        contents; it only terminates write-access to the drawing buffer early.
        During the same frame, {{GPUCanvasContext/getCurrentTexture()}} continues returning the
        same destroyed texture.

        [$Expire the current texture$] sets the currentTexture to `null`.
        It is called by {{GPUCanvasContext/configure()}}, resizing the canvas,
        presentation, {{OffscreenCanvas/transferToImageBitmap()}}, and others.

    : <dfn>\[[lastPresentedImage]]</dfn>, of type `(readonly image)?`, initially `null`
    ::
        The image most recently presented for this canvas in "[$updating the rendering of a WebGPU canvas$]".
        If the device is lost or destroyed, this image **may** be used as a fallback in
        "[$get a copy of the image contents of a context$]" in order to prevent the canvas from going blank.

        Note:
        This property only needs to exist in implementations which implement the fallback, which is optional.
</dl>

{{GPUCanvasContext}} has the following methods:

<dl dfn-type=method dfn-for=GPUCanvasContext>
    : <dfn>configure(configuration)</dfn>
    ::
        Configures the context for this canvas.
        This clears the drawing buffer to transparent black (in [$Replace the drawing buffer$]).

        <div algorithm=GPUCanvasContext.configure>
            <div data-timeline=content>
                **Called on:** {{GPUCanvasContext}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUCanvasContext/configure(configuration)">
                    |configuration|: Desired configuration for the context.
                </pre>

                **Returns:** undefined

                [=Content timeline=] steps:

                1. Let |device| be |configuration|.{{GPUCanvasConfiguration/device}}.
                1. [=?=] [$Validate texture format required features$] of
                    |configuration|.{{GPUCanvasConfiguration/format}} with |device|.{{GPUObjectBase/[[device]]}}.
                1. [=?=] [$Validate texture format required features$] of each element of
                    |configuration|.{{GPUCanvasConfiguration/viewFormats}} with |device|.{{GPUObjectBase/[[device]]}}.
                1. If [=Supported context formats=] does not [=set/contain=]
                            |configuration|.{{GPUCanvasConfiguration/format}}, throw a {{TypeError}}.
                1. Let |descriptor| be the
                    [$GPUTextureDescriptor for the canvas and configuration$](|this|.{{GPUCanvasContext/canvas}}, |configuration|).
                1. Set |this|.{{GPUCanvasContext/[[configuration]]}} to |configuration|.

                    <div class=note heading>
                        This spec requires supporting HDR via the {{GPUCanvasConfiguration/toneMapping}} option.
                        If a user agent only supports <code>toneMapping: "standard"</code>,
                        then the {{GPUCanvasConfiguration/toneMapping}} member should *not* exist in
                        {{GPUCanvasConfiguration}}, so it will not exist on the object returned by
                        {{GPUCanvasContext/getConfiguration()}} and will not be accessed by
                        {{GPUCanvasContext/configure()}}). This allows websites to detect feature
                        support.
                    </div>

                1. Set |this|.{{GPUCanvasContext/[[textureDescriptor]]}} to |descriptor|.
                1. [$Replace the drawing buffer$] of |this|.
                1. Issue the subsequent steps on the [=Device timeline=] of |device|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. If any of the following requirements are unmet, [$generate a validation error$] and return.

                    <div class=validusage>
                        - [$validating GPUTextureDescriptor$](|device|, |descriptor|)
                            must return true.
                    </div>

                    Note: This early validation remains valid until the next
                    {{GPUCanvasContext/configure()}} call, **except** for
                    validation of the {{GPUTextureDescriptor/size}}, which changes when
                    the canvas is resized.
            </div>
        </div>

    : <dfn>unconfigure()</dfn>
    ::
        Removes the context configuration. Destroys any textures produced while configured.

        <div algorithm=GPUCanvasContext.unconfigure>
            <div data-timeline=content>
                **Called on:** {{GPUCanvasContext}} |this|.

                **Returns:** undefined

                [=Content timeline=] steps:

                1. Set |this|.{{GPUCanvasContext/[[configuration]]}} to `null`.
                1. Set |this|.{{GPUCanvasContext/[[textureDescriptor]]}} to `null`.
                1. [$Replace the drawing buffer$] of |this|.
            </div>
        </div>

    : <dfn>getConfiguration()</dfn>
    ::
        Returns the context configuration.

        <div algorithm=GPUCanvasContext.getConfiguration>
            <div data-timeline=content>
                **Called on:** {{GPUCanvasContext}} |this|.

                **Returns:** {{GPUCanvasConfiguration}} or `null`

                [=Content timeline=] steps:

                1. Let |configuration| be a copy of |this|.{{GPUCanvasContext/[[configuration]]}}.
                1. Return |configuration|.
            </div>
        </div>

        <div class=note heading>
            In scenarios where {{GPUCanvasContext/getConfiguration()}} shows that
            {{GPUCanvasConfiguration/toneMapping}} is implemented and the <l>'@media/dynamic-range'</l> media
            query indicates HDR support, then WebGPU canvas **should** render content using the full
            HDR range instead of clamping values to the SDR range of the HDR display.
        </div>

    : <dfn>getCurrentTexture()</dfn>
    ::
        Get the {{GPUTexture}} that will be composited to the document by the {{GPUCanvasContext}}
        next.

        <div class=note heading>
            An application **should** call {{GPUCanvasContext/getCurrentTexture()}}
            in the same task that renders to the canvas texture.
            Otherwise, the texture could get destroyed by these steps before the
            application is finished rendering to it.

            The expiry task (defined below) is optional to implement.
            Even if implemented, task source priority is not normatively defined, so may happen as
            early as the next task, or as late as after all other task sources are empty
            (see [=automatic expiry task source=]).
            Expiry is only guaranteed when a visible canvas is displayed
            ([$updating the rendering of a WebGPU canvas$]) and in other
            callers of "[$Expire the current texture$]".
        </div>

        <div algorithm=GPUCanvasContext.getCurrentTexture>
            <div data-timeline=content>
                **Called on:** {{GPUCanvasContext}} |this|.

                **Returns:** {{GPUTexture}}

                [=Content timeline=] steps:

                1. If |this|.{{GPUCanvasContext/[[configuration]]}} is `null`,
                    throw an {{InvalidStateError}} and return.
                1. [=Assert=] |this|.{{GPUCanvasContext/[[textureDescriptor]]}} is not `null`.
                1. Let |device| be |this|.{{GPUCanvasContext/[[configuration]]}}.{{GPUCanvasConfiguration/device}}.
                1. If |this|.{{GPUCanvasContext/[[currentTexture]]}} is `null`:
                    1. [$Replace the drawing buffer$] of |this|.
                    1. Set |this|.{{GPUCanvasContext/[[currentTexture]]}} to the result of calling
                        |device|.{{GPUDevice/createTexture()}} with |this|.{{GPUCanvasContext/[[textureDescriptor]]}},
                        except with the {{GPUTexture}}'s underlying storage pointing to
                        |this|.{{GPUCanvasContext/[[drawingBuffer]]}}.

                        Note:
                        If the texture can't be created (e.g. due to validation failure or out-of-memory),
                        this generates and error and returns an [$invalidated$] {{GPUTexture}}.
                        Some validation here is redundant with that done in {{GPUCanvasContext/configure()}}.
                        Implementations **must not** skip this redundant validation.
                1. **Optionally**, [$queue an automatic expiry task$] with device |device| and the following steps:

                    <div data-timeline=content>
                        1. [$Expire the current texture$] of |this|.

                            Note: If this already happened when
                            [$updating the rendering of a WebGPU canvas$], it has no effect.
                    </div>
                1. Return |this|.{{GPUCanvasContext/[[currentTexture]]}}.
            </div>
        </div>

        Note: The same {{GPUTexture}} object will be returned by every
        call to {{GPUCanvasContext/getCurrentTexture()}} until "[$Expire the current texture$]"
        runs, even if that {{GPUTexture}} is destroyed, failed validation, or failed to allocate.
</dl>

<div algorithm data-timeline=content>
    To <dfn abstract-op>get a copy of the image contents of a context</dfn>:

    **Arguments:**

    - |context|: the {{GPUCanvasContext}}

    **Returns:** image contents

    [=Content timeline=] steps:

    1. Let |snapshot| be a transparent black image of the same size as |context|.{{GPUCanvasContext/canvas}}.
    1. Let |configuration| be |context|.{{GPUCanvasContext/[[configuration]]}}.
    1. If |configuration| is `null`:
        1. Return |snapshot|.

        Note: The configuration will be `null` if the context has not been
        configured or has been {{GPUCanvasContext/unconfigure()|unconfigured}}. This is identical to
        the behavior when the canvas has no context.

    1. Ensure that all submitted work items (e.g. queue submissions) have
        completed writing to the image (via |context|.{{GPUCanvasContext/[[currentTexture]]}}).
    1. If |configuration|.{{GPUCanvasConfiguration/device}} is found to be [=valid=]:

        1. Set |snapshot| to a copy of the |context|.{{GPUCanvasContext/[[drawingBuffer]]}}.

        Otherwise, if |context|.{{GPUCanvasContext/[[lastPresentedImage]]}} is not `null`:

        1. **Optionally**, set |snapshot| to a copy of |context|.{{GPUCanvasContext/[[lastPresentedImage]]}}.

            Note:
            This is optional because the {{GPUCanvasContext/[[lastPresentedImage]]}} may no longer exist,
            depending on what caused device loss.
            Implementations may choose to skip it even if do they still have access to that image.

    1. Let |alphaMode| be |configuration|.{{GPUCanvasConfiguration/alphaMode}}.
    1. If |alphaMode| is {{GPUCanvasAlphaMode/"opaque"}}:
        1. Clear the alpha channel of |snapshot| to 1.0.

            Note:
            If the {{GPUCanvasContext/[[currentTexture]]}}, if any, has been destroyed
            (for example in "[$Expire the current texture$]"), the alpha channel is unobservable,
            and implementations may clear the alpha channel in-place.
        1. Tag |snapshot| as being opaque.

        Otherwise:
        1. Tag |snapshot| with |alphaMode|.
    1. Tag |snapshot| with the {{GPUCanvasConfiguration/colorSpace}} and
        {{GPUCanvasConfiguration/toneMapping}} of |configuration|.
    1. Return |snapshot|.

    <!-- POSTV1(desynchronized): If a "desynchronized" option is added, explicitly describe its behavior here. -->
</div>

<div algorithm data-timeline=content>
    To <dfn abstract-op>Replace the drawing buffer</dfn> of a {{GPUCanvasContext}} |context|, run
    the following [=content timeline=] steps:

    1. [$Expire the current texture$] of |context|.
    1. Let |configuration| be |context|.{{GPUCanvasContext/[[configuration]]}}.
    1. Set |context|.{{GPUCanvasContext/[[drawingBuffer]]}} to a transparent black image of the same
        size as |context|.{{GPUCanvasContext/canvas}}.

        - If |configuration| is null, the drawing buffer is tagged with the color space
            {{PredefinedColorSpace/"srgb"}}.
            In this case, the drawing buffer will remain blank until the context is configured.
        - If not, the drawing buffer has the specified
            |configuration|.{{GPUCanvasConfiguration/format}} and is tagged with the specified
            |configuration|.{{GPUCanvasConfiguration/colorSpace}} and
            |configuration|.{{GPUCanvasConfiguration/toneMapping}}.

        Note: |configuration|.{{GPUCanvasConfiguration/alphaMode}} is ignored until
        "[$get a copy of the image contents of a context$]".

        <div class=note heading>
            A newly replaced drawing buffer image behaves as if it is cleared to transparent black,
            but, like after {{GPUStoreOp/"discard"}}, an implementation can clear it lazily only
            if it becomes necessary.
        </div>

        Note: This will often be a no-op, if the drawing buffer is already cleared
        and has the correct configuration.
</div>

<div algorithm data-timeline=content>
    To <dfn abstract-op>Expire the current texture</dfn> of a {{GPUCanvasContext}} |context|, run
    the following [=content timeline=] steps:

    1. If |context|.{{GPUCanvasContext/[[currentTexture]]}} is not `null`:
        1. Call |context|.{{GPUCanvasContext/[[currentTexture]]}}.{{GPUTexture/destroy()}}
            (without destroying |context|.{{GPUCanvasContext/[[drawingBuffer]]}})
            to terminate write access to the image.
        1. Set |context|.{{GPUCanvasContext/[[currentTexture]]}} to `null`.
</div>

## HTML Specification Hooks ## {#canvas-hooks}

The following algorithms "hook" into algorithms in the HTML specification, and must run at the
specified points.

<div algorithm="get the bitmap of a WebGPU canvas" data-timeline=content>
    When the "bitmap" is read from an {{HTMLCanvasElement}} or {{OffscreenCanvas}} with a
    {{GPUCanvasContext}} |context|, run the following [=content timeline=] steps:

    1. Return [$get a copy of the image contents of a context|a copy of the image contents$]
        of |context|.

    <div class=note heading>
        This occurs in many places, including:

        - When an {{HTMLCanvasElement}} has its rendering updated.
            - Including when the canvas is the [=placeholder canvas element=] of an {{OffscreenCanvas}}.
        - When {{OffscreenCanvas/transferToImageBitmap()}} creates an {{ImageBitmap}} from the bitmap.
            (See also [$transferToImageBitmap from WebGPU$].)
        - When WebGPU canvas contents are read using other Web APIs, like
            {{CanvasDrawImage/drawImage()}}, `texImage2D()`, `texSubImage2D()`,
            {{HTMLCanvasElement/toDataURL()}}, {{HTMLCanvasElement/toBlob()}}, and so on.

        If {{GPUCanvasConfiguration/alphaMode}} is {{GPUCanvasAlphaMode/"opaque"}},
        this incurs a clear of the alpha channel. Implementations may skip this step when
        they are able to read or display images in a way that ignores the alpha channel.

        If an application needs a canvas only for interop (not presentation), avoid
        {{GPUCanvasAlphaMode/"opaque"}} if it is not needed.
    </div>
</div>

<div algorithm data-timeline=content>
    When <dfn abstract-op>updating the rendering of a WebGPU canvas</dfn>
    (an {{HTMLCanvasElement}} or an {{OffscreenCanvas}} with a [=placeholder canvas element=])
    with a {{GPUCanvasContext}} |context|, which occurs before getting the canvas's image contents,
    in the following sub-steps of the [=event loop processing model=]:

    - "update the rendering or user interface of that `Document`"
    - "update the rendering of that dedicated worker"

    Note:
    Service and Shared workers do not have "update the rendering" steps
    because they cannot render to user-visible canvases.
    {{AnimationFrameProvider/requestAnimationFrame()}} is not exposed in
    {{ServiceWorkerGlobalScope}} and {{SharedWorkerGlobalScope}}, and
    {{OffscreenCanvas}}es from {{HTMLCanvasElement/transferControlToOffscreen()}}
    [cannot be sent to these workers](https://github.com/whatwg/html/issues/10112).

    Run the following [=content timeline=] steps:

    1. [$Expire the current texture$] of |context|.

        Note: If this already happened in the task queued by
        {{GPUCanvasContext/getCurrentTexture()}}, it has no effect.
    1. Set |context|.{{GPUCanvasContext/[[lastPresentedImage]]}} to
        |context|.{{GPUCanvasContext/[[drawingBuffer]]}}.

        Note: This is just a reference, not a copy; the drawing buffer's contents can't change
        in-place after the current texture has expired.

    Note:
    This does not happen for standalone {{OffscreenCanvas}}es (created by `new OffscreenCanvas()`).
</div>

<div algorithm data-timeline=content>
    <dfn abstract-op>transferToImageBitmap from WebGPU</dfn>:

    When {{OffscreenCanvas/transferToImageBitmap()}} is called on a canvas with
    {{GPUCanvasContext}} |context|, after creating an {{ImageBitmap}} from the canvas's bitmap,
    run the following [=content timeline=] steps:

    1. [$Replace the drawing buffer$] of |context|.

    Note: This makes {{OffscreenCanvas/transferToImageBitmap()}}
    equivalent to "moving" (and possibly alpha-clearing) the image contents into the
    ImageBitmap, without a copy.
</div>

- The [$update the canvas size$] algorithm. <!-- TODO: Move it here for clarity. -->

## GPUCanvasConfiguration ## {#canvas-configuration}

The <dfn dfn>supported context formats</dfn> are the [=set=] of {{GPUTextureFormat}}s:
&laquo;{{GPUTextureFormat/"bgra8unorm"}}, {{GPUTextureFormat/"rgba8unorm"}},
{{GPUTextureFormat/"rgba16float"}}&raquo;. These formats must be supported when specified as a
{{GPUCanvasConfiguration}}.{{GPUCanvasConfiguration/format}} regardless of the given
{{GPUCanvasConfiguration}}.{{GPUCanvasConfiguration/device}}.

Note: Canvas configuration cannot use `srgb` formats like {{GPUTextureFormat/"bgra8unorm-srgb"}}.
Instead, use the non-`srgb` equivalent ({{GPUTextureFormat/"bgra8unorm"}}), specify the `srgb`
format in the {{GPUCanvasConfiguration/viewFormats}}, and use {{GPUTexture/createView()}} to create
a view with an `srgb` format.

<script type=idl>
enum GPUCanvasAlphaMode {
    "opaque",
    "premultiplied",
};

enum GPUCanvasToneMappingMode {
    "standard",
    "extended",
};

dictionary GPUCanvasToneMapping {
  GPUCanvasToneMappingMode mode = "standard";
};

dictionary GPUCanvasConfiguration {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = 0x10;  // GPUTextureUsage.RENDER_ATTACHMENT
    sequence<GPUTextureFormat> viewFormats = [];
    PredefinedColorSpace colorSpace = "srgb";
    GPUCanvasToneMapping toneMapping = {};
    GPUCanvasAlphaMode alphaMode = "opaque";
};
</script>

{{GPUCanvasConfiguration}} has the following members:

<dl dfn-type=dict-member dfn-for=GPUCanvasConfiguration>
    : <dfn>device</dfn>
    ::
        The {{GPUDevice}} that textures returned by {{GPUCanvasContext/getCurrentTexture()}} will be
        compatible with.

    : <dfn>format</dfn>
    ::
        The format that textures returned by {{GPUCanvasContext/getCurrentTexture()}} will have.
        Must be one of the [=Supported context formats=].

    : <dfn>usage</dfn>
    ::
        The usage that textures returned by {{GPUCanvasContext/getCurrentTexture()}} will have.
        {{GPUTextureUsage/RENDER_ATTACHMENT}} is the default, but is not automatically included
        if the usage is explicitly set. Be sure to include {{GPUTextureUsage/RENDER_ATTACHMENT}}
        when setting a custom usage if you wish to use textures returned by
        {{GPUCanvasContext/getCurrentTexture()}} as color targets for a render pass.

    : <dfn>viewFormats</dfn>
    ::
        The formats that views created from textures returned by
        {{GPUCanvasContext/getCurrentTexture()}} may use.

    : <dfn>colorSpace</dfn>
    ::
        The color space that values written into textures returned by
        {{GPUCanvasContext/getCurrentTexture()}} should be displayed with.

    : <dfn>toneMapping</dfn>
    ::
        The tone mapping determines how the content of textures returned by
        {{GPUCanvasContext/getCurrentTexture()}} are to be displayed.

        Note: If an implementation doesn't support HDR WebGPU canvases, it should also not expose this member, to allow for feature detection. See {{GPUCanvasContext/getConfiguration()}}.

    : <dfn>alphaMode</dfn>
    ::
        Determines the effect that alpha values will have on the content of textures returned by
        {{GPUCanvasContext/getCurrentTexture()}} when read, displayed, or used as an image source.
</dl>

<div class=example>
    Configure a {{GPUCanvasContext}} to be used with a specific {{GPUDevice}}, using the preferred
    format for this context:

    <pre highlight=js>
        const canvas = document.createElement('canvas');
        const context = canvas.getContext('webgpu');

        context.configure({
            device: gpuDevice,
            format: navigator.gpu.getPreferredCanvasFormat(),
        });
    </pre>
</div>

<div algorithm data-timeline=content>
    The <dfn abstract-op>GPUTextureDescriptor for the canvas and configuration</dfn>(
    ({{HTMLCanvasElement}} or {{OffscreenCanvas}}) |canvas|,
    {{GPUCanvasConfiguration}} |configuration|)
    is a {{GPUTextureDescriptor}} with the following members:

    - {{GPUTextureDescriptor/size}}: [|canvas|.width, |canvas|.height, 1].
    - {{GPUTextureDescriptor/format}}: |configuration|.{{GPUCanvasConfiguration/format}}.
    - {{GPUTextureDescriptor/usage}}: |configuration|.{{GPUCanvasConfiguration/usage}}.
    - {{GPUTextureDescriptor/viewFormats}}: |configuration|.{{GPUCanvasConfiguration/viewFormats}}.

    and other members set to their defaults.

    |canvas|.width refers to {{HTMLCanvasElement}}.{{HTMLCanvasElement/width}} or {{OffscreenCanvas}}.{{OffscreenCanvas/width}}.
    |canvas|.height refers to {{HTMLCanvasElement}}.{{HTMLCanvasElement/height}} or {{OffscreenCanvas}}.{{OffscreenCanvas/height}}.
</div>

### Canvas Color Space ### {#canvas-color-space}

During presentation, the color values in the canvas are converted to the color
space of the screen.

The {{GPUCanvasConfiguration/toneMapping}} determines the handling of values
outside of the `[0, 1]` interval in the color space of the screen.

### Canvas Context sizing ### {#context-sizing}

All canvas configuration is set in {{GPUCanvasContext/configure()}} except for the resolution
of the canvas, which is set by the canvas's `width` and `height`.

Note:
Like WebGL and 2d canvas, resizing a WebGPU canvas loses the current contents of the drawing buffer.
In WebGPU, it does so by [$Replace the drawing buffer|replacing the drawing buffer$].

<div algorithm data-timeline=content>
    When an {{HTMLCanvasElement}} or {{OffscreenCanvas}} |canvas| with a
    {{GPUCanvasContext}} |context| has its `width` or `height` attributes set,
    <dfn abstract-op>update the canvas size</dfn> by running the following
    [=content timeline=] steps:

    1. [$Replace the drawing buffer$] of |context|.
    1. Let |configuration| be |context|.{{GPUCanvasContext/[[configuration]]}}
    1. If |configuration| is not `null`:
        1. Set |context|.{{GPUCanvasContext/[[textureDescriptor]]}} to the
            [$GPUTextureDescriptor for the canvas and configuration$](|canvas|, |configuration|).

    Note: This may result in a {{GPUTextureDescriptor}} which exceeds the
    {{supported limits/maxTextureDimension2D}} of the device. In this case,
    validation will fail inside {{GPUCanvasContext/getCurrentTexture()}}.

    Note: This algorithm is run any time the |canvas| `width` or `height` attributes are set, even
    if their value is not changed.
</div>

<h3 id=gpucanvastonemappingmode data-dfn-type=enum>`GPUCanvasToneMappingMode`
</h3>

This enum specifies how color values are displayed to the screen.

<dl dfn-type=enum-value dfn-for=GPUCanvasToneMappingMode>
    : <dfn>"standard"</dfn>
    ::
        Color values within the standard dynamic range of the screen are unchanged, and
        all other color values are projected to the standard dynamic range of the screen.

        Note:
        This projection is often accomplished by clamping color values in the color space
        of the screen to the `[0, 1]` interval.

        <div class=example>
            For example, suppose that the value `(1.035, -0.175, -0.140)` is written to an
            `'srgb'` canvas.

            If this is presented to an sRGB screen, then this will be converted to sRGB
            (which is a no-op, because the canvas is sRGB), then projected into the display's space.
            Using component-wise clamping, this results in the sRGB value `(1.0, 0.0, 0.0)`.

            If this is presented to a Display P3 screen, then this will be converted to
            the value `(0.948, 0.106, 0.01)` in the Display P3 color space, and no
            clamping will be needed.
        </div>

    : <dfn>"extended"</dfn>
    ::
        Color values in the extended dynamic range of the screen are unchanged, and all
        other color values are projected to the extended dynamic range of the screen.

        Note:
        This projection is often accomplished by clamping color values in the color space of
        the screen to the interval of values that the screen is capable of displaying,
        which may include values greater than `1`.

        <div class=example>
            For example, suppose that the value `(2.5, -0.15, -0.15)` is written to an
            `'srgb'` canvas.

            If this is presented to an sRGB screen that is capable of displaying values
            in the `[0, 4]` interval in sRGB space, then this will be converted to sRGB
            (which is a no-op, because the canvas is sRGB), then projected into the display's space.
            If using component-wise clamping, this results in the sRGB value `(2.5, 0.0, 0.0)`.

            If this is presented to a Display P3 screen that is capable of displaying
            values in the `[0, 2]` interval in Display P3 space, then this will be
            converted to the value `(2.3, 0.545, 0.386)` in the Display P3 color space,
            then projected into the display's space.
            If using component-wise clamping, this results in the Display P3 value `(2.0, 0.545, 0.386)`.
        </div>

</dl>

<h3 id=gpucanvasalphamode data-dfn-type=enum>`GPUCanvasAlphaMode`
<span id=GPUCanvasAlphaMode></span>
</h3>

This enum selects how the contents of the canvas will be interpreted when read, when
[$get a copy of the image contents of a context|displayed to the screen or used as an image source$]
(in drawImage, toDataURL, etc.)

Below, `src` is a value in the canvas texture, and `dst` is an image that the canvas
is being composited into (e.g. an HTML page rendering, or a 2D canvas).

<dl dfn-type=enum-value dfn-for=GPUCanvasAlphaMode>
    : <dfn>"opaque"</dfn>
    ::
        Read RGB as opaque and ignore alpha values.
        If the content is not already opaque, the alpha channel is cleared to 1.0
        in "[$get a copy of the image contents of a context$]".

    : <dfn>"premultiplied"</dfn>
    ::
        Read RGBA as premultiplied: color values are premultiplied by their alpha value.
        100% red at 50% alpha is `[0.5, 0, 0, 0.5]`.

        If the canvas texture contains [=out-of-gamut premultiplied RGBA values=] at the time the
        canvas contents are read, the behavior depends on whether the canvas is:

        <dl class=switch>
            : [$get a copy of the image contents of a context|used as an image source$]
            :: Values are preserved, as described in [[#color-space-conversions|color space conversion]].

            : displayed to the screen
            :: Compositing results are undefined.

                Note:
                This is true even if color space conversion would produce in-gamut values before
                compositing, because the intermediate format for compositing is not specified.
        </dl>
</dl>

# Errors &amp; Debugging # {#errors-and-debugging}

During the normal course of operation of WebGPU, errors are raised via [$dispatch error$].

After a device is [=lose the device|lost=], errors are no longer surfaced, where possible.
After this point, implementations do not need to run validation or error tracking:

- The validity of objects on the device becomes unobservable.
- {{GPUDevice/popErrorScope()}} and {{GPUDevice/uncapturederror}} stop reporting errors.
    (No errors are generated by the device loss itself.
    Instead, the {{GPUDevice}}.{{GPUDevice/lost}} promise resolves to indicate the device is lost.)
- All operations which send a message back to the [=content timeline=] will skip their usual steps.
    Most will appear to succeed, except for {{GPUBuffer/mapAsync()}}, which produces an error
    because it is impossible to provide the correct mapped data after the device has been lost.

    This makes it unobservable whether other types of operations (that don't send messages back)
    actually execute or not.

## Fatal Errors ## {#fatal-errors}

<script type=idl>
enum GPUDeviceLostReason {
    "unknown",
    "destroyed",
};

[Exposed=(Window, Worker), SecureContext]
interface GPUDeviceLostInfo {
    readonly attribute GPUDeviceLostReason reason;
    readonly attribute DOMString message;
};

partial interface GPUDevice {
    readonly attribute Promise<GPUDeviceLostInfo> lost;
};
</script>

{{GPUDevice}} has the following additional attributes:

<dl dfn-type=attribute dfn-for=GPUDevice>
    : <dfn>lost</dfn>
    ::
        A [=slot-backed attribute=] holding a promise which is created with the device, remains
        pending for the lifetime of the device, then resolves when the device is lost.

        Upon initialization, it is set to [=a new promise=].
</dl>

<h3 id=gpuerror data-dfn-type=interface>`GPUError`
<span id=error></span>
</h3>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUError {
    readonly attribute DOMString message;
};
</script>

{{GPUError}} is the base interface for all errors surfaced from {{GPUDevice/popErrorScope()}}
and the {{GPUDevice/uncapturederror}} event.

Errors must only be generated for operations that explicitly state the conditions one may
be generated under in their respective algorithms, and the subtype of error that is generated.

No errors are generated from a device which is lost.
See [[#errors-and-debugging]].

Note: {{GPUError}} may gain new subtypes in future versions of this spec. Applications should handle
this possibility, using only the error's {{GPUError/message}} when possible, and specializing using
`instanceof`. Use `error.constructor.name` when it's necessary to serialize an error (e.g. into
JSON, for a debug report).

{{GPUError}} has the following [=immutable properties=]:

<dl dfn-type=attribute dfn-for=GPUError data-timeline=const>
    : <dfn>message</dfn>
    ::
        A human-readable, [=localizable text=] message providing information about the error that
        occurred.

        Note: This message is generally intended for application developers to debug their
        applications and capture information for debug reports, not to be surfaced to end-users.

        Note: User agents should not include potentially machine-parsable details in this message,
        such as free system memory on {{GPUErrorFilter/"out-of-memory"}} or other details about the
        conditions under which memory was exhausted.

        Note: The {{GPUError/message}} should follow the [=best practices for language and
        direction information=]. This includes making use of any future standards which may emerge
        regarding the reporting of string language and direction metadata.

        <p class="note editorial"><span class=marker>Editorial note:</span>
        At the time of this writing, no language/direction recommendation is available that provides
        compatibility and consistency with legacy APIs, but when there is, adopt it formally.
</dl>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUValidationError
        : GPUError {
    constructor(DOMString message);
};
</script>

{{GPUValidationError}} is a subtype of {{GPUError}} which indicates that an operation did not
satisfy all validation requirements. Validation errors are always indicative of an application
error, and is expected to fail the same way across all devices assuming the same
{{device/[[features]]}} and {{device/[[limits]]}} are in use.

<div algorithm data-timeline=device>
    To <dfn abstract-op lt="Generate a validation error|generate a validation error|validation error">generate a
    validation error</dfn> for {{GPUDevice}} |device|, run the following steps:

    [=Device timeline=] steps:

    1. Let |error| be a new {{GPUValidationError}} with an appropriate error message.
    1. [$Dispatch error$] |error| to |device|.
</div>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUOutOfMemoryError
        : GPUError {
    constructor(DOMString message);
};
</script>

{{GPUOutOfMemoryError}} is a subtype of {{GPUError}} which indicates that there was not enough free
memory to complete the requested operation. The operation may succeed if attempted again with a
lower memory requirement (like using smaller texture dimensions), or if memory used by other
resources is released first.

<div algorithm data-timeline=device>
    To <dfn abstract-op lt="Generate an out-of-memory error|generate an out-of-memory error|out-of-memory error">
    generate an out-of-memory error</dfn> for {{GPUDevice}} |device|, run the following steps:

    [=Device timeline=] steps:

    1. Let |error| be a new {{GPUOutOfMemoryError}} with an appropriate error message.
    1. [$Dispatch error$] |error| to |device|.
</div>

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUInternalError
        : GPUError {
    constructor(DOMString message);
};
</script>

{{GPUInternalError}} is a subtype of {{GPUError}} which indicates than an operation failed for a
system or implementation-specific reason even when all validation requirements have been satisfied.
For example, the operation may exceed the capabilities of the implementation in a way not easily
captured by the [=supported limits=]. The same operation may succeed on other devices or under
difference circumstances.

<div algorithm data-timeline=device>
    To <dfn abstract-op lt="Generate an internal error|generate an internal error|internal error">generate an
    internal error</dfn> for {{GPUDevice}} |device|, run the following steps:

    [=Device timeline=] steps:

    1. Let |error| be a new {{GPUInternalError}} with an appropriate error message.
    1. [$Dispatch error$] |error| to |device|.
</div>

## Error Scopes ## {#error-scopes}

A <dfn dfn>GPU error scope</dfn> captures {{GPUError}}s that were generated while the
[=GPU error scope=] was current. Error scopes are used to isolate errors that occur within a set
of WebGPU calls, typically for debugging purposes or to make an operation more fault tolerant.

[=GPU error scope=] has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for="GPU error scope" data-timeline=device>
    : <dfn>\[[errors]]</dfn>, of type [=list=]&lt;{{GPUError}}&gt;, initially []
    ::
        The {{GPUError}}s, if any, observed while the [=GPU error scope=] was current.

    : <dfn>\[[filter]]</dfn>, of type {{GPUErrorFilter}}
    ::
        Determines what type of {{GPUError}} this [=GPU error scope=] observes.
</dl>

<script type=idl>
enum GPUErrorFilter {
    "validation",
    "out-of-memory",
    "internal",
};

partial interface GPUDevice {
    undefined pushErrorScope(GPUErrorFilter filter);
    Promise<GPUError?> popErrorScope();
};
</script>

{{GPUErrorFilter}} defines the type of errors that should be caught when calling
{{GPUDevice/pushErrorScope()}}:

<dl dfn-type=enum-value dfn-for=GPUErrorFilter>
    : <dfn>"validation"</dfn>
    ::
        Indicates that the error scope will catch a {{GPUValidationError}}.

    : <dfn>"out-of-memory"</dfn>
    ::
        Indicates that the error scope will catch a {{GPUOutOfMemoryError}}.

    : <dfn>"internal"</dfn>
    ::
        Indicates that the error scope will catch a {{GPUInternalError}}.
</dl>

{{GPUDevice}} has the following [=device timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUDevice data-timeline=device>
    : <dfn>\[[errorScopeStack]]</dfn>, of type [=stack=]&lt;[=GPU error scope=]&gt;
    ::
        A [=stack=] of [=GPU error scopes=] that have been pushed to the {{GPUDevice}}.
</dl>

<div algorithm data-timeline=device>
    The <dfn abstract-op>current error scope</dfn> for a {{GPUError}} |error| and {{GPUDevice}}
    |device| is determined by issuing the following steps to the [=device timeline=] of |device|:

    [=Device timeline=] steps:

    1. If |error| is an instance of:

        <dl class=switch>
            : {{GPUValidationError}}
            :: Let |type| be "validation".
            : {{GPUOutOfMemoryError}}
            :: Let |type| be "out-of-memory".
            : {{GPUInternalError}}
            :: Let |type| be "internal".
        </dl>
    1. Let |scope| be the last [=list/item=] of |device|.{{GPUDevice/[[errorScopeStack]]}}.
    1. While |scope| is not `undefined`:
        1. If |scope|.{{GPU error scope/[[filter]]}} is |type|, return |scope|.
        1. Set |scope| to the previous [=list/item=] of
            |device|.{{GPUDevice/[[errorScopeStack]]}}.
    1. Return `undefined`.
</div>

<div algorithm>
    To <dfn abstract-op lt="Dispatch error|dispatch error">dispatch an error</dfn> {{GPUError}}
    |error| on {{GPUDevice}} |device|, run the following [=device timeline=] steps:

    <div data-timeline=device>
        [=Device timeline=] steps:

        Note: No errors are generated from a device which is lost.
        If this algorithm is called while
        |device| is [$invalid|lost$], it will not be observable to the application.
        See [[#errors-and-debugging]].

        1. Let |scope| be the [$current error scope$] for |error| and |device|.
        1. If |scope| is not `undefined`:
            1. [=list/Append=] |error| to |scope|.{{GPU error scope/[[errors]]}}.
            1. Return.

            Otherwise, issue the following steps to the [=content timeline=]:
    </div>
    <div data-timeline=content>
        [=Content timeline=] steps:

        1. If the user agent chooses, [$queue a global task for GPUDevice$] |device|
            with the following steps:

            <div data-timeline=content>
                1. Fire a {{GPUUncapturedErrorEvent}} named "{{GPUDevice/uncapturederror}}" on
                    |device|, with an {{GPUUncapturedErrorEvent/error}} of |error|.
            </div>

        Note: After dispatching the event, user agents **should** surface uncaptured errors to
        developers, for example as warnings in the browser's developer console, unless the event's
        {{Event/defaultPrevented}} is true. In other words, calling {{Event/preventDefault()}}
        on the event should silence the console warning.
    </div>

    Note: The user agent may choose to throttle or limit the number of {{GPUUncapturedErrorEvent}}s
    that a {{GPUDevice}} can raise to prevent an excessive amount of error handling or logging from
    impacting performance.
</div>

<dl dfn-type=method dfn-for=GPUDevice>
    : <dfn>pushErrorScope(filter)</dfn>
    ::
        Pushes a new [=GPU error scope=] onto the {{GPUDevice/[[errorScopeStack]]}} for |this|.

        <div algorithm=GPUDevice.pushErrorScope>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Arguments:**

                <pre class=argumentdef for="GPUDevice/pushErrorScope(filter)">
                    |filter|: Which class of errors this error scope observes.
                </pre>

                **Returns:** {{undefined}}

                [=Content timeline=] steps:

                1. Issue the subsequent steps on the [=Device timeline=] of |this|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] steps:

                1. Let |scope| be a new [=GPU error scope=].
                1. Set |scope|.{{GPU error scope/[[filter]]}} to |filter|.
                1. [=stack/Push=] |scope| onto |this|.{{GPUDevice/[[errorScopeStack]]}}.
            </div>
        </div>

    : <dfn>popErrorScope()</dfn>
    ::
        Pops a [=GPU error scope=] off the {{GPUDevice/[[errorScopeStack]]}} for |this|
        and resolves to **any** {{GPUError}} observed by the error scope, or `null` if none.

        There is no guarantee of the ordering of promise resolution.

        <div algorithm=GPUDevice.popErrorScope>
            <div data-timeline=content>
                **Called on:** {{GPUDevice}} |this|.

                **Returns:** {{Promise}}&lt;{{GPUError}}?&gt;

                [=Content timeline=] steps:

                1. Let <var data-timeline=content>contentTimeline</var> be the current [=Content timeline=].
                1. Let |promise| be [=a new promise=].
                1. Issue the |check steps| on the [=Device timeline=] of |this|.
                1. Return |promise|.
            </div>
            <div data-timeline=device>
                [=Device timeline=] |check steps|:

                1. If |this| is [$invalid|lost$]:

                    1. Issue the following steps on
                        <var data-timeline=content>contentTimeline</var>:

                        <div data-timeline=content>
                            [=Content timeline=] steps:

                            1. [=Resolve=] |promise| with `null`.
                        </div>

                    1. Return.

                    Note: No errors are generated from a device which is lost.
                    See [[#errors-and-debugging]].
                1. If any of the following requirements are unmet:

                    <div class=validusage>
                        - |this|.{{GPUDevice/[[errorScopeStack]]}}.[=list/size=] must be &gt; 0.
                    </div>

                    Then issue the following steps on <var data-timeline=content>contentTimeline</var>
                    and return:

                    <div data-timeline=content>
                        [=Content timeline=] steps:

                        1. [=Reject=] |promise| with an {{OperationError}}.
                    </div>

                1. Let |scope| be the result of [=stack/pop|popping=] an [=list/item=] off of
                    |this|.{{GPUDevice/[[errorScopeStack]]}}.
                1. Let |error| be **any** one of the items in |scope|.{{GPU error scope/[[errors]]}},
                    or `null` if there are none.

                    For any two errors E1 and E2 in the list, if E2 was caused by E1, E2 **should
                    not** be the one selected.

                    Note:
                    For example, if E1 comes from `t` = {{GPUDevice/createTexture()}}, and
                    E2 comes from `t`.{{GPUTexture/createView()}} because `t` was [$invalid$],
                    E1 should be be preferred since it will be easier for a developer to understand
                    what went wrong.
                    Since both of these are {{GPUValidationError}}s, the only difference will be in
                    the {{GPUError/message}} field, which is meant only to be read by humans anyway.

                1. At an **unspecified point now or in the future**,
                    issue the subsequent steps on <var data-timeline=content>contentTimeline</var>.

                    Note:
                    By allowing {{GPUDevice/popErrorScope()}} calls to resolve in any order, with
                    any of the errors observed by the scope, this spec allows validation to complete
                    out of order, as long as any state observations are made at the appropriate
                    point in adherence to this spec. For example, this allows implementations to
                    perform shader compilation, which depends only on non-stateful inputs, to be
                    completed on a background thread in parallel with other device-timeline work,
                    and report any resulting errors later.
            </div>
            <div data-timeline=content>
                [=Content timeline=] steps:

                1. [=Resolve=] |promise| with |error|.
            </div>
        </div>
</dl>

<div class=example>
    Using error scopes to capture validation errors from a {{GPUDevice}} operation that may fail:

    <pre highlight=js>
        gpuDevice.pushErrorScope('validation');

        let sampler = gpuDevice.createSampler({
            maxAnisotropy: 0, // Invalid, maxAnisotropy must be at least 1.
        });

        gpuDevice.popErrorScope().then((error) => {
            if (error) {
                // There was an error creating the sampler, so discard it.
                sampler = null;
                console.error(\`An error occured while creating sampler: ${error.message}\`);
            }
        });
    </pre>
</div>

<div class=note heading>
Error scopes can encompass as many commands as needed. The number of commands an error scope covers
will generally be correlated to what sort of action the application intends to take in response to
an error occuring.

For example: An error scope that only contains the creation of a single resource, such as a texture
or buffer, can be used to detect failures such as out of memory conditions, in which case the
application may try freeing some resources and trying the allocation again.

Error scopes do not identify which command failed, however. So, for instance, wrapping all the
commands executed while loading a model in a single error scope will not offer enough granularity to
determine if the issue was due to memory constraints. As a result freeing resources would usually
not be a productive response to a failure of that scope. A more appropriate response would be to
allow the application to fall back to a different model or produce a warning that the model could
not be loaded. If responding to memory constraints is desired, the operations allocating memory can
always be wrapped in a smaller nested error scope.
</div>

## Telemetry ## {#telemetry}

When a {{GPUError}} is generated that is not observed by any [=GPU error scope=], the user agent **may** [=fire an event=] named <dfn event for=GPUDevice>uncapturederror</dfn> at a {{GPUDevice}} using {{GPUUncapturedErrorEvent}}.

Note: {{GPUDevice/uncapturederror}} events are intended to be used for telemetry and reporting
unexpected errors. They may not be dispatched for all uncaptured errors (for example, there may be a limit on the number of errors surfaced), and should not be used for handling known error cases that may occur during
normal operation of an application. Prefer using {{GPUDevice/pushErrorScope()}} and
{{GPUDevice/popErrorScope()}} in those cases.

<script type=idl>
[Exposed=(Window, Worker), SecureContext]
interface GPUUncapturedErrorEvent : Event {
    constructor(
        DOMString type,
        GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict
    );
    [SameObject] readonly attribute GPUError error;
};

dictionary GPUUncapturedErrorEventInit : EventInit {
    required GPUError error;
};
</script>

{{GPUUncapturedErrorEvent}} has the following attributes:

<dl dfn-type=attribute dfn-for=GPUUncapturedErrorEvent data-timeline=content>
    : <dfn>error</dfn>
    ::
        A [=slot-backed attribute=] holding an object representing the error that was uncaptured.
        This has the same type as errors returned by {{GPUDevice/popErrorScope()}}.
</dl>

<script type=idl>
partial interface GPUDevice {
    attribute EventHandler onuncapturederror;
};
</script>

{{GPUDevice}} has the following [=content timeline properties=]:

<dl dfn-type=attribute dfn-for=GPUDevice data-timeline=content>
    : <dfn>onuncapturederror</dfn>
    ::
        An [=event handler IDL attribute=] for the {{GPUDevice/uncapturederror}} event type.
</dl>

<div class=example>
    Listening for uncaptured errors from a {{GPUDevice}}:

    <pre highlight=js>
        gpuDevice.addEventListener('uncapturederror', (event) => {
            // Re-surface the error, because adding an event listener may silence console logs.
            console.error('A WebGPU error was not captured:', event.error);

            myEngineDebugReport.uncapturedErrors.push({
                type: event.error.constructor.name,
                message: event.error.message,
            });
        });
    </pre>
</div>

# Detailed Operations # {#detailed-operations}

This section describes the details of various GPU operations.

## Computing ## {#computing-operations}

Computing operations provide direct access to GPU's programmable hardware.
Compute shaders do not have shader stage inputs or outputs; their results are
side effects from writing data into storage bindings bound either as
{{GPUBufferBindingLayout}} with {{GPUBufferBindingType}} {{GPUBufferBindingType/"storage"}}
or as {{GPUStorageTextureBindingLayout}}.
These operations are encoded within {{GPUComputePassEncoder}} as:

- {{GPUComputePassEncoder/dispatchWorkgroups()}}
- {{GPUComputePassEncoder/dispatchWorkgroupsIndirect()}}

The main compute algorithm:

<div algorithm data-timeline=queue>
    <dfn abstract-op>compute</dfn>(|descriptor|, |dispatchCall|)

    **Arguments:**

    - |descriptor|: Description of the current {{GPUComputePipeline}}.
    - |dispatchCall|: The dispatch call parameters. May come from function arguments or an {{GPUBufferUsage/INDIRECT}} buffer.

    1. Let |computeInvocations| be an [=list/empty=] [=list=].
    1. Let |computeStage| be |descriptor|.{{GPUComputePipelineDescriptor/compute}}.
    1. Let |workgroupSize| be the computed workgroup size for |computeStage|.{{GPUProgrammableStage/entryPoint}} after
        applying |computeStage|.{{GPUProgrammableStage/constants}} to |computeStage|.{{GPUProgrammableStage/module}}.
    1. For |workgroupX| in range <code>[0, |dispatchCall|.`workgroupCountX`]</code>:
        1. For |workgroupY| in range <code>[0, |dispatchCall|.`workgroupCountY`]</code>:
            1. For |workgroupZ| in range <code>[0, |dispatchCall|.`workgroupCountZ`]</code>:
                1. For |localX| in range <code>[0, |workgroupSize|.`x`]</code>:
                    1. For |localY| in range <code>[0, |workgroupSize|.`y`]</code>:
                        1. For |localZ| in range <code>[0, |workgroupSize|.`y`]</code>:
                            1. Let |invocation| be <code>{ |computeStage|, |workgroupX|, |workgroupY|, |workgroupZ|, |localX|, |localY|, |localZ| }</code>
                            1. [=list/Append=] |invocation| to |computeInvocations|.

    1. For every |invocation| in |computeInvocations|, in any order the [=device=] chooses, including in parallel:
        1. Set the shader [=builtins=]:
            - Set the [=builtin/num_workgroups=] builtin, if any, to <code>(<br/>
                    |dispatchCall|.`workgroupCountX`,<br/>
                    |dispatchCall|.`workgroupCountY`,<br/>
                    |dispatchCall|.`workgroupCountZ`<br/>
                )</code>
            - Set the [=builtin/workgroup_id=] builtin, if any, to <code>(<br/>
                    |invocation|.|workgroupX|,<br/>
                    |invocation|.|workgroupY|,<br/>
                    |invocation|.|workgroupZ|<br/>
                )</code>
            - Set the [=builtin/local_invocation_id=] builtin, if any, to <code>(<br/>
                    |invocation|.|localX|,<br/>
                    |invocation|.|localY|,<br/>
                    |invocation|.|localZ|<br/>
                )</code>
            - Set the [=builtin/global_invocation_id=] builtin, if any, to <code>(<br/>
                    |invocation|.|workgroupX| * |workgroupSize|.`x` + |invocation|.|localX|,<br/>
                    |invocation|.|workgroupY| * |workgroupSize|.`y` + |invocation|.|localY|,<br/>
                    |invocation|.|workgroupZ| * |workgroupSize|.`z` + |invocation|.|localZ|<br/>
                )</code>.
            - Set the [=builtin/local_invocation_index=] builtin, if any, to <code>
                |invocation|.|localX| + (|invocation|.|localY| * |workgroupSize|.`x`) +
                (|invocation|.|localZ| * |workgroupSize|.`x` * |workgroupSize|.`y`)
                </code>
        1. Invoke the compute shader entry point described by |invocation|.|computeStage|.

    Note: Shader invocations have no guaranteed order, and will generally run in parallel according to device
    capabilities. Developers should not assume that any given invocation or workgroup will complete before any
    other one is started. Some devices may appear to execute in a consistent order, but this behavior should not
    be relied on as it will not perform identically across all devices. Shaders that require synchronization
    across invocations must use [=Synchronization Built-in Functions=] to coordinate execution.
</div>

The [=device=] may become [=lose the device|lost=] if
[=shader execution end|shader execution does not end=]
in a reasonable amount of time, as determined by the user agent.

## Rendering ## {#rendering-operations}

Rendering is done by a set of GPU operations that are executed within {{GPURenderPassEncoder}},
and result in modifications of the texture data, viewed by the render pass attachments.
These operations are encoded with:

- {{GPURenderCommandsMixin/draw()}}
- {{GPURenderCommandsMixin/drawIndexed()}},
- {{GPURenderCommandsMixin/drawIndirect()}}
- {{GPURenderCommandsMixin/drawIndexedIndirect()}}.

Note: rendering is the traditional use of GPUs, and is supported by multiple fixed-function
blocks in hardware.

The main rendering algorithm:

<div algorithm data-timeline=queue>
    <dfn abstract-op>render</dfn>(pipeline, drawCall, state)

        **Arguments:**

        - |pipeline|: The current {{GPURenderPipeline}}.
        - |drawCall|: The draw call parameters. May come from function arguments or an {{GPUBufferUsage/INDIRECT}} buffer.
        - |state|: [=RenderState=] of the {{GPURenderCommandsMixin}} where the draw call is issued.

        1. Let |descriptor| be |pipeline|.{{GPURenderPipeline/[[descriptor]]}}.

        1. **Resolve indices**. See [[#index-resolution]].

            Let |vertexList| be the result of [$resolve indices$](|drawCall|, |state|).

        1. **Process vertices**. See [[#vertex-processing]].

            Execute [$process vertices$](|vertexList|, |drawCall|, |descriptor|.{{GPURenderPipelineDescriptor/vertex}}, |state|).

        1. **Assemble primitives**. See [[#primitive-assembly]].

            Execute [$assemble primitives$](|vertexList|, |drawCall|, |descriptor|.{{GPURenderPipelineDescriptor/primitive}}).

        1. **Clip primitives**. See [[#primitive-clipping]].

            Let |primitiveList| be the result of this stage.

        1. **Rasterize**. See [[#rasterization]].

            Let |rasterizationList| be the result of [$rasterize$](|primitiveList|, |state|).

        1. **Process fragments**. See [[#fragment-processing]].

            Gather a list of |fragments|, resulting from executing
            [$process fragment$](|rasterPoint|, |descriptor|, |state|)
            for each |rasterPoint| in |rasterizationList|.

        1. **Write pixels**. See [[#output-merging]].

            For each non-null |fragment| of |fragments|:
                - Execute [$process depth stencil$](|fragment|, |pipeline|, |state|).
                - Execute [$process color attachments$](|fragment|, |pipeline|, |state|).
</div>

### Index Resolution ### {#index-resolution}

At the first stage of rendering, the pipeline builds
a list of vertices to process for each instance.

<div algorithm data-timeline=queue>
    <dfn abstract-op>resolve indices</dfn>(drawCall, state)

    **Arguments:**

    - |drawCall|: The draw call parameters. May come from function arguments or an {{GPUBufferUsage/INDIRECT}} buffer.
    - |state|: The snapshot of the {{GPURenderCommandsMixin}} state at the time of the draw call.

    **Returns:** list of integer indices.

    1. Let |vertexIndexList| be an empty list of indices.
    1. If |drawCall| is an indexed draw call:
        1. Initialize the |vertexIndexList| with |drawCall|.indexCount integers.
        1. For |i| in range 0 .. |drawCall|.indexCount (non-inclusive):
            1. Let |relativeVertexIndex| be [$fetch index$](|i| + |drawCall|.`firstIndex`,
                |state|.{{GPURenderCommandsMixin/[[index_buffer]]}}).
            1. If |relativeVertexIndex| has the special value `"out of bounds"`,
                return the empty list.

                Note: Implementations may choose to display a warning when this occurs,
                especially when it is easy to detect (like in non-indirect indexed draw calls).
            1. Append |drawCall|.`baseVertex` + |relativeVertexIndex| to the |vertexIndexList|.

        Otherwise:
        1. Initialize the |vertexIndexList| with |drawCall|.vertexCount integers.
        1. Set each |vertexIndexList| item |i| to the value |drawCall|.firstVertex + |i|.
    1. Return |vertexIndexList|.

    Note: in the case of indirect draw calls, the `indexCount`, `vertexCount`,
    and other properties of |drawCall| are read from the indirect buffer
    instead of the draw command itself.
</div>

<div algorithm data-timeline=queue>
    <dfn abstract-op>fetch index</dfn>(i, buffer, offset, format)

    **Arguments:**

    - |i|: Index of a vertex index to fetch.
    - |state|: The snapshot of the {{GPURenderCommandsMixin}} state at the time of the draw call.

    **Returns:** unsigned integer or `"out of bounds"`

    1. Let |indexSize| be defined by the |state|.{{GPURenderCommandsMixin/[[index_format]]}}:

        <dl class=switch>
            : {{GPUIndexFormat/"uint16"}}
            :: 2
            : {{GPUIndexFormat/"uint32"}}
            :: 4
        </dl>
    1. If |state|.{{GPURenderCommandsMixin/[[index_buffer_offset]]}} +
        |i + 1| &times; |indexSize| &gt; |state|.{{GPURenderCommandsMixin/[[index_buffer_size]]}},
        return the special value `"out of bounds"`.
    1. Interpret the data in |state|.{{GPURenderCommandsMixin/[[index_buffer]]}}, starting at offset
        |state|.{{GPURenderCommandsMixin/[[index_buffer_offset]]}} + |i| &times; |indexSize|,
        of size |indexSize| bytes, as an unsigned integer and return it.
</div>

### Vertex Processing ### {#vertex-processing}

Vertex processing stage is a programmable stage of the render [=pipeline=] that
processes the vertex attribute data, and produces
clip space positions for [[#primitive-clipping]], as well as other data for the
[[#fragment-processing]].

<div algorithm data-timeline=queue>
    <dfn abstract-op>process vertices</dfn>(vertexIndexList, drawCall, desc, state)

    **Arguments:**

    - |vertexIndexList|: List of vertex indices to process (mutable, passed by reference).
    - |drawCall|: The draw call parameters. May come from function arguments or an {{GPUBufferUsage/INDIRECT}} buffer.
    - |desc|: The descriptor of type {{GPUVertexState}}.
    - |state|: The snapshot of the {{GPURenderCommandsMixin}} state at the time of the draw call.

    Each vertex |vertexIndex| in the |vertexIndexList|,
    in each instance of index |rawInstanceIndex|, is processed independently.
    The |rawInstanceIndex| is in range from 0 to |drawCall|.instanceCount - 1, inclusive.
    This processing happens in parallel, and any side effects, such as
    writes into {{GPUBufferBindingType}} {{GPUBufferBindingType/"storage"}} bindings,
    may happen in any order.

    1. Let |instanceIndex| be |rawInstanceIndex| + |drawCall|.firstInstance.
    1. For each non-`null` |vertexBufferLayout| in the list of |desc|.{{GPUVertexState/buffers}}:
        1. Let |i| be the index of the buffer layout in this list.
        1. Let |vertexBuffer|, |vertexBufferOffset|, and |vertexBufferBindingSize| be the
            buffer, offset, and size at slot |i| of |state|.{{GPURenderCommandsMixin/[[vertex_buffers]]}}.
        1. Let |vertexElementIndex| be dependent on |vertexBufferLayout|.{{GPUVertexBufferLayout/stepMode}}:

            <dl class=switch>
                : {{GPUVertexStepMode/"vertex"}}
                :: |vertexIndex|
                : {{GPUVertexStepMode/"instance"}}
                :: |instanceIndex|
            </dl>
        1. Let |drawCallOutOfBounds| be `false`.
        1. For each |attributeDesc| in |vertexBufferLayout|.{{GPUVertexBufferLayout/attributes}}:
            1. Let |attributeOffset| be |vertexBufferOffset| +
                |vertexElementIndex| * |vertexBufferLayout|.{{GPUVertexBufferLayout/arrayStride}} +
                |attributeDesc|.{{GPUVertexAttribute/offset}}.
            1. If |attributeOffset| + [$GPUVertexFormat/byteSize$](|attributeDesc|.{{GPUVertexAttribute/format}}) &gt;
                |vertexBufferOffset| + |vertexBufferBindingSize|:
                1. Set |drawCallOutOfBounds| to `true`.
                1. <strong>Optionally ([=implementation-defined=])</strong>,
                    [=list/empty=] |vertexIndexList| and return, cancelling the draw call.

                    Note: This allows implementations to detect out-of-bounds values in the index buffer
                    before issuing a draw call, instead of using [=invalid memory reference=] behavior.
        1. For each |attributeDesc| in |vertexBufferLayout|.{{GPUVertexBufferLayout/attributes}}:
            1. If |drawCallOutOfBounds| is `true`:
                1. Load the attribute |data| according to WGSL's [=invalid memory reference=]
                    behavior, from |vertexBuffer|.

                    Note: [=Invalid memory reference=] allows several behaviors, including actually
                    loading the "correct" result for an attribute that is in-bounds, even when
                    the draw-call-wide |drawCallOutOfBounds| is `true`.

                Otherwise:

                1. Let |attributeOffset| be |vertexBufferOffset| +
                    |vertexElementIndex| * |vertexBufferLayout|.{{GPUVertexBufferLayout/arrayStride}} +
                    |attributeDesc|.{{GPUVertexAttribute/offset}}.
                1. Load the attribute |data| of format |attributeDesc|.{{GPUVertexAttribute/format}}
                    from |vertexBuffer| starting at offset |attributeOffset|.
                    The components are loaded in the order `x`, `y`, `z`, `w` from buffer memory.
            1. Convert the |data| into a shader-visible format, according to [=channel formats=] rules.

                <div class=example>
                    An attribute of type {{GPUVertexFormat/"snorm8x2"}} and byte values of `[0x70, 0xD0]`
                    will be converted to `vec2<f32>(0.88, -0.38)` in WGSL.
                </div>
            1. Adjust the |data| size to the shader type:
                - if both are scalar, or both are vectors of the same dimensionality, no adjustment is needed.
                - if |data| is vector but the shader type is scalar, then only the first component is extracted.
                - if both are vectors, and |data| has a higher dimension, the extra components are dropped.

                    <div class=example>
                        An attribute of type {{GPUVertexFormat/"float32x3"}} and value `vec3<f32>(1.0, 2.0, 3.0)`
                        will exposed to the shader as `vec2<f32>(1.0, 2.0)` if a 2-component vector is expected.
                    </div>
                - if the shader type is a vector of higher dimensionality, or the |data| is a scalar,
                    then the missing components are filled from `vec4<*>(0, 0, 0, 1)` value.

                    <div class=example>
                        An attribute of type {{GPUVertexFormat/"sint32"}} and value `5` will be exposed
                        to the shader as `vec4<i32>(5, 0, 0, 1)` if a 4-component vector is expected.
                    </div>
            1. Bind the |data| to vertex shader input
                location |attributeDesc|.{{GPUVertexAttribute/shaderLocation}}.
    1. For each {{GPUBindGroup}} group at |index| in |state|.{{GPUBindingCommandsMixin/[[bind_groups]]}}:
        1. For each resource {{GPUBindingResource}} in the bind group:
            1. Let |entry| be the corresponding {{GPUBindGroupLayoutEntry}} for this resource.
            1. If |entry|.{{GPUBindGroupLayoutEntry/visibility}} includes {{GPUShaderStage/VERTEX}}:
                - Bind the resource to the shader under group |index| and binding {{GPUBindGroupLayoutEntry/binding|GPUBindGroupLayoutEntry.binding}}.
    1. Set the shader [=builtins=]:
        - Set the `vertex_index` builtin, if any, to |vertexIndex|.
        - Set the `instance_index` builtin, if any, to |instanceIndex|.
    1. Invoke the vertex shader entry point described by |desc|.

        Note: The target platform caches the results of vertex shader invocations.
        There is no guarantee that any |vertexIndex| that repeats more than once will
        result in multiple invocations. Similarly, there is no guarantee that a single |vertexIndex|
        will only be processed once.

        The [=device=] may become [=lose the device|lost=] if
        [=shader execution end|shader execution does not end=]
        in a reasonable amount of time, as determined by the user agent.
</div>

### Primitive Assembly ### {#primitive-assembly}

Primitives are assembled by a fixed-function stage of GPUs.

<div algorithm data-timeline=queue>
    <dfn abstract-op>assemble primitives</dfn>(vertexIndexList, drawCall, desc)

    **Arguments:**

    - |vertexIndexList|: List of vertex indices to process.
    - |drawCall|: The draw call parameters. May come from function arguments or an {{GPUBufferUsage/INDIRECT}} buffer.
    - |desc|: The descriptor of type {{GPUPrimitiveState}}.

    For each instance, the primitives get assembled from the vertices that have been
    processed by the shaders, based on the |vertexIndexList|.

    1. First, if the primitive topology is a strip, (which means that
        |desc|.{{GPUPrimitiveState/stripIndexFormat}} is not undefined)
        and the |drawCall| is indexed, the |vertexIndexList| is split into
        sub-lists using the maximum value of |desc|.{{GPUPrimitiveState/stripIndexFormat}}
        as a separator.

        Example: a |vertexIndexList| with values `[1, 2, 65535, 4, 5, 6]` of type {{GPUIndexFormat/"uint16"}}
        will be split in sub-lists `[1, 2]` and `[4, 5, 6]`.

    1. For each of the sub-lists |vl|, primitive generation is done according to the
        |desc|.{{GPUPrimitiveState/topology}}:

        <dl class=switch>
            : {{GPUPrimitiveTopology/"line-list"}}
            ::
                Line primitives are composed from (|vl|.0, |vl|.1),
                then (|vl|.2, |vl|.3), then (|vl|.4 to |vl|.5), etc.
                Each subsequent primitive takes 2 vertices.

            : {{GPUPrimitiveTopology/"line-strip"}}
            ::
                Line primitives are composed from (|vl|.0, |vl|.1),
                then (|vl|.1, |vl|.2), then (|vl|.2, |vl|.3), etc.
                Each subsequent primitive takes 1 vertex.

            : {{GPUPrimitiveTopology/"triangle-list"}}
            ::
                Triangle primitives are composed from (|vl|.0, |vl|.1, |vl|.2),
                then (|vl|.3, |vl|.4, |vl|.5), then (|vl|.6, |vl|.7, |vl|.8), etc.
                Each subsequent primitive takes 3 vertices.

            : {{GPUPrimitiveTopology/"triangle-strip"}}
            ::
                Triangle primitives are composed from (|vl|.0, |vl|.1, |vl|.2),
                then (|vl|.2, |vl|.1, |vl|.3), then (|vl|.2, |vl|.3, |vl|.4),
                then (|vl|.4, |vl|.3, |vl|.5), etc.
                Each subsequent primitive takes 1 vertices.
        </dl>

        Any incomplete primitives are dropped.
</div>

### Primitive Clipping ### {#primitive-clipping}

Vertex shaders have to produce a built-in [=position builtin|position=] (of type `vec4<f32>`),
which denotes the <dfn dfn>clip position</dfn> of a vertex in [=clip space coordinates=].

Primitives are clipped to the <dfn dfn>clip volume</dfn>, which, for any [=clip position=] |p|
inside a primitive, is defined by the following inequalities:

- &minus;|p|.w &le; |p|.x &le; |p|.w
- &minus;|p|.w &le; |p|.y &le; |p|.w
- 0 &le; |p|.z &le; |p|.w (<dfn dfn>depth clipping</dfn>)

When the {{GPUFeatureName/"clip-distances"}} feature is enabled, this [=clip volume=] can
be further restricted by user-defined half-spaces by declaring [=builtin/clip_distances=] in the
output of vertex stage. Each value in the [=builtin/clip_distances=] array will be linearly
interpolated across the primitive, and the portion of the primitive with interpolated distances less
than 0 will be clipped.

If |descriptor|.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/unclippedDepth}} is `true`,
[=depth clipping=] is not applied: the [=clip volume=] is not bounded in the z dimension.

A primitive passes through this stage unchanged if every one of its edges
lie entirely inside the [=clip volume=].
If the edges of a primitives intersect the boundary of the [=clip volume=],
the intersecting edges are reconnected by new edges that lie along the boundary of the [=clip volume=].
For triangular primitives (|descriptor|.{{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/topology}} is
{{GPUPrimitiveTopology/"triangle-list"}} or {{GPUPrimitiveTopology/"triangle-strip"}}), this reconnection
may result in introduction of new vertices into the polygon, internally.

If a primitive intersects an edge of the [=clip volume=]’s boundary,
the clipped polygon must include a point on this boundary edge.

If the vertex shader outputs other floating-point values (scalars and vectors), qualified with
"perspective" interpolation, they also get clipped.
The output values associated with a vertex that lies within the clip volume are unaffected by clipping.
If a primitive is clipped, however, the output values assigned to vertices produced by clipping are clipped.

Considering an edge between vertices |a| and |b| that got clipped, resulting in the vertex |c|,
let's define |t| to be the ratio between the edge vertices:
|c|.p = |t| &times; |a|.p &plus; (1 &minus; |t|) &times; |b|.p,
where |x|.p is the output [=clip position=] of a vertex |x|.

For each vertex output value "v" with a corresponding fragment input,
|a|.v and |b|.v would be the outputs for |a| and |b| vertices respectively.
The clipped shader output |c|.v is produced based on the interpolation qualifier:
<dl class=switch>
    : [=interpolation type/flat=]
    ::
        Flat interpolation is unaffected, and is based on the <dfn dfn>provoking vertex</dfn>,
        which is determined by the [=interpolation sampling=] mode declared in the shader. The
        output value is the same for the whole primitive, and matches the vertex output of the
        [=provoking vertex=].

    : [=interpolation type/linear=]
    ::
        The interpolation ratio gets adjusted against the perspective coordinates of the
        [=clip position=]s, so that the result of interpolation is linear in screen space.

    : [=interpolation type/perspective=]
    ::
        The value is linearly interpolated in clip space, producing perspective-correct values.
</dl>

The result of primitive clipping is a new set of primitives, which are contained
within the [=clip volume=].

### Rasterization ### {#rasterization}

Rasterization is the hardware processing stage that maps the generated primitives
to the 2-dimensional rendering area of the <dfn dfn>framebuffer</dfn> -
the set of render attachments in the current {{GPURenderPassEncoder}}.
This rendering area is split into an even grid of pixels.

The [=framebuffer=] coordinates start from the top-left corner of the render targets.
Each unit corresponds exactly to one pixel. See [[#coordinate-systems]] for more information.

Rasterization determines the set of pixels affected by a primitive. In case of multi-sampling,
each pixel is further split into
|descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}} samples.
The <dfn dfn noexport>standard sample patterns</dfn> are as follows,
with positions in framebuffer coordinates relative to the top-left corner of the pixel,
such that the pixel ranges from (0, 0) to (1, 1):

<table class=data>
    <thead>
        <tr><th>{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}}<th>Sample positions
    <tbody>
        <tr><td>1<td>
            Sample 0: (0.5, 0.5)
        <tr><td>4<td>
            Sample 0: (0.375, 0.125)<br>
            Sample 1: (0.875, 0.375)<br>
            Sample 2: (0.125, 0.625)<br>
            Sample 3: (0.625, 0.875)
</table>

Implementations must use the [=standard sample pattern=] for the given
{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}} when performing rasterization.

Let's define a <dfn dfn>FragmentDestination</dfn> to contain:
<dl dfn-for=FragmentDestination>
    : <dfn dfn>position</dfn>
    :: the 2D pixel position using [=framebuffer coordinates=]
    : <dfn dfn>sampleIndex</dfn>
    :: an integer in case [[#per-sample-shading]] is active,
        or `null` otherwise
</dl>

We'll also use a notion of [=NDC|normalized device coordinates=], or NDC.
In this coordinate system, the viewport bounds range in X and Y from -1 to 1, and in Z from 0 to 1.

Rasterization produces a list of <dfn dfn>RasterizationPoint</dfn>s, each containing the following data:
<dl dfn-for=RasterizationPoint>
    : <dfn dfn>destination</dfn>
    :: refers to [=FragmentDestination=]
    : <dfn dfn>coverageMask</dfn>
    :: refers to multisample coverage mask (see [[#sample-masking]])
    : <dfn dfn>frontFacing</dfn>
    :: is true if it's a point on the front face of a primitive
    : <dfn dfn>perspectiveDivisor</dfn>
    :: refers to interpolated 1.0 &divide; W across the primitive
    : <dfn dfn>depth</dfn>
    :: refers to the depth in [=viewport coordinates=],
        i.e. between the {{RenderState/[[viewport]]}} `minDepth` and `maxDepth`.
    : <dfn dfn>primitiveVertices</dfn>
    :: refers to the list of vertex outputs forming the primitive
    : <dfn dfn>barycentricCoordinates</dfn>
    :: refers to [[#barycentric-coordinates]]
</dl>

<div algorithm data-timeline=queue>
    <dfn abstract-op>rasterize</dfn>(primitiveList, state)

    **Arguments:**

    - |primitiveList|: List of primitives to rasterize.
    - |state|: The active [=RenderState=].

    **Returns:** list of [=RasterizationPoint=].

    Each primitive in |primitiveList| is processed independently.
    However, the order of primitives affects later stages, such as depth/stencil operations and pixel writes.

    1. First, the clipped vertices are transformed into [=NDC=] - normalized device coordinates.
        Given the output position |p|, the [=NDC=] position and perspective divisor are:

        ndc(|p|) = vector(|p|.x &divide; |p|.w, |p|.y &divide; |p|.w, |p|.z &divide; |p|.w)

        divisor(|p|) = 1.0 &divide; |p|.w

    1. Let |vp| be |state|.{{RenderState/[[viewport]]}}.
        Map the [=NDC=] position |n| into [=viewport coordinates=]:
        * Compute [=framebuffer=] coordinates from the render target offset and size:

            framebufferCoords(|n|) = vector(|vp|.`x` &plus; 0.5 &times; (|n|.x &plus; 1) &times; |vp|.`width`, |vp|.`y` &plus; 0.5 &times; (&minus;|n|.y &plus; 1) &times; |vp|.`height`)

        * Compute depth by linearly mapping [0,1] to the viewport depth range:

            depth(|n|) = |vp|.`minDepth` &plus; |n|.`z` &times; ( |vp|.`maxDepth` - |vp|.`minDepth` )

    1. Let |rasterizationPoints| be the list of points, each having its attributes (`divisor(p)`,
        `framebufferCoords(n)`, `depth(n)`, etc.) interpolated according to its position on the
        primitive, using the same interpolation as [[#primitive-clipping]]. If the attribute is
        user-defined (not a [=built-in output value=]) then the [=interpolation type=] specified by
        the [=@interpolate=] WGSL attribute is used.

    1. Proceed with a specific rasterization algorithm,
        depending on {{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/topology}}:

        <dl class=switch>
            : {{GPUPrimitiveTopology/"point-list"}}
            :: The point, if not filtered by [[#primitive-clipping]], goes into [[#point-rasterization]].
            : {{GPUPrimitiveTopology/"line-list"}} or {{GPUPrimitiveTopology/"line-strip"}}
            :: The line cut by [[#primitive-clipping]] goes into [[#line-rasterization]].
            : {{GPUPrimitiveTopology/"triangle-list"}} or {{GPUPrimitiveTopology/"triangle-strip"}}
            :: The polygon produced in [[#primitive-clipping]] goes into [[#polygon-rasterization]].
        </dl>

    1. Remove all the points |rp| from |rasterizationPoints| that have
        |rp|.[=RasterizationPoint/destination=].[=FragmentDestination/position=]
        outside of |state|.{{RenderState/[[scissorRect]]}}.

    1. Return |rasterizationPoints|.
</div>

#### Point Rasterization #### {#point-rasterization}

A single [=FragmentDestination=] is selected within the pixel containing the
[=framebuffer=] coordinates of the point.

The coverage mask depends on multi-sampling mode:
<dl class=switch>
    : sample-frequency
    :: coverageMask = 1 &Lt; `sampleIndex`
    : pixel-frequency multi-sampling
    :: coverageMask = 1 &Lt; |descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}} &minus; 1
    : no multi-sampling
    :: coverageMask = 1
</dl>

#### Line Rasterization #### {#line-rasterization}

The exact algorithm used for line rasterization is not defined, and may differ between
implementations. For example, the line may be drawn using [[#polygon-rasterization]] of a 1px-width
rectangle around the line segment, or using Bresenham's line algorithm to select the
[=FragmentDestination=]s.

Note: See [[vulkan#primsrast-lines-basic|Basic Line Segment Rasterization]] and
[[vulkan#primsrast-lines-bresenham|Bresenham Line Segment Rasterization]] in the [[vulkan inline]]
spec for more details of how line these line rasterization algorithms may be implemented.

#### Barycentric coordinates #### {#barycentric-coordinates}

Barycentric coordinates is a list of |n| numbers |b|<sub>|i|</sub>,
defined for a point |p| inside a convex polygon with |n| vertices |v|<sub>|i|</sub> in [=framebuffer=] space.
Each |b|<sub>|i|</sub> is in range 0 to 1, inclusive, and represents the proximity to vertex |v|<sub>|i|</sub>.
Their sum is always constant:

&sum; (|b|<sub>|i|</sub>) = 1

These coordinates uniquely specify any point |p| within the polygon (or on its boundary) as:

|p| = &sum; (|b|<sub>|i|</sub> &times; |p|<sub>|i|</sub>)

For a polygon with 3 vertices - a triangle,
barycentric coordinates of any point |p| can be computed as follows:

|A|<sub>polygon</sub> = A(|v|<sub>|1|</sub>, |v|<sub>|2|</sub>, |v|<sub>|3|</sub>)
|b|<sub>|1|</sub> = A(|p|, |b|<sub>|2|</sub>, |b|<sub>|3|</sub>) &divide; |A|<sub>polygon</sub>
|b|<sub>|2|</sub> = A(|b|<sub>|1|</sub>, |p|, |b|<sub>|3|</sub>) &divide; |A|<sub>polygon</sub>
|b|<sub>|3|</sub> = A(|b|<sub>|1|</sub>, |b|<sub>|2|</sub>, |p|) &divide; |A|<sub>polygon</sub>

Where A(list of points) is the area of the polygon with the given set of vertices.

For polygons with more than 3 vertices, the exact algorithm is implementation-dependent.
One of the possible implementations is to triangulate the polygon and compute the barycentrics
of a point based on the triangle it falls into.

#### Polygon Rasterization #### {#polygon-rasterization}

A polygon is <dfn dfn>front-facing</dfn> if it's oriented towards the projection.
Otherwise, the polygon is <dfn dfn>back-facing</dfn>.

<div algorithm data-timeline=queue>
    <dfn abstract-op>rasterize polygon</dfn>()

    **Arguments:**

    **Returns:** list of [=RasterizationPoint=].

    1. Let |rasterizationPoints| be an empty list.
    1. Let |v|(|i|) be the [=framebuffer=] coordinates for the clipped vertex number |i| (starting with 1)
        in a rasterized polygon of |n| vertices.

        Note: this section uses the term "polygon" instead of a "triangle",
        since [[#primitive-clipping]] stage may have introduced additional vertices.
        This is non-observable by the application.

    1. Determine if the polygon is front-facing,
        which depends on the sign of the |area| occupied by the polygon in [=framebuffer=] coordinates:

        |area| = 0.5 &times; ((|v|<sub>1</sub>.x &times; |v|<sub>|n|</sub>.y &minus; |v|<sub>|n|</sub>.x &times; |v|<sub>1</sub>.y) &plus; &sum; (|v|<sub>|i|&plus;1</sub>.x &times; |v|<sub>|i|</sub>.y &minus; |v|<sub>|i|</sub>.x &times; |v|<sub>|i|&plus;1</sub>.y))

        The sign of |area| is interpreted based on the {{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/frontFace}}:

        <dl class=switch>
            : {{GPUFrontFace/"ccw"}}
            :: |area| &gt; 0 is considered [=front-facing=], otherwise [=back-facing=]
            : {{GPUFrontFace/"cw"}}
            :: |area| &lt; 0 is considered [=front-facing=], otherwise [=back-facing=]
        </dl>

    1. Cull based on {{GPURenderPipelineDescriptor/primitive}}.{{GPUPrimitiveState/cullMode}}:

        <dl class=switch>
            : {{GPUCullMode/"none"}}
            :: All polygons pass this test.
            : {{GPUCullMode/"front"}}
            :: The [=front-facing=] polygons are discarded,
                and do not process in later stages of the render pipeline.
            : {{GPUCullMode/"back"}}
            :: The [=back-facing=] polygons are discarded.
        </dl>

    1. Determine a set of [=fragments=] inside the polygon in [=framebuffer=] space -
        these are locations scheduled for the per-fragment operations.
        This operation is known as "point sampling".
        The logic is based on |descriptor|.{{GPURenderPipelineDescriptor/multisample}}:

        <dl class=switch>
            : disabled
            :: [=Fragment=]s are associated with pixel centers. That is, all the points with coordinates |C|, where
                fract(|C|) = vector2(0.5, 0.5) in the [=framebuffer=] space, enclosed into the polygon, are included.
                If a pixel center is on the edge of the polygon, whether or not it's included is not defined.

                Note: this becomes a subject of precision for the rasterizer.

            : enabled
            :: Each pixel is associated with |descriptor|.{{GPURenderPipelineDescriptor/multisample}}.{{GPUMultisampleState/count}}
                locations, which are [=implementation-defined=].
                The locations are ordered, and the list is the same for each pixel of the [=framebuffer=].
                Each location corresponds to one fragment in the multisampled [=framebuffer=].

                The rasterizer builds a mask of locations being hit inside each pixel and provides is as "sample-mask"
                built-in to the fragment shader.
        </dl>

    1. For each produced fragment of type [=FragmentDestination=]:

        1. Let |rp| be a new [=RasterizationPoint=] object
        1. Compute the list |b| as [[#barycentric-coordinates]] of that fragment.
            Set |rp|.[=RasterizationPoint/barycentricCoordinates=] to |b|.
        1. Let |d|<sub>|i|</sub> be the depth value of |v|<sub>|i|</sub>.
        1. Set |rp|.[=RasterizationPoint/depth=] to &sum; (|b|<sub>|i|</sub> &times; |d|<sub>|i|</sub>)
        1. Append |rp| to |rasterizationPoints|.

    1. Return |rasterizationPoints|.
</div>

### Fragment Processing ### {#fragment-processing}

The fragment processing stage is a programmable stage of the render [=pipeline=] that
computes the fragment data (often a color) to be written into render targets.

This stage produces a <dfn dfn>Fragment</dfn> for each [=RasterizationPoint=]:
<div algorithm="Fragment accessors" dfn-for=Fragment>
    - <dfn dfn>destination</dfn> refers to [=FragmentDestination=].
    - <dfn dfn>frontFacing</dfn> is true if it's a fragment on the front face of a primitive.
    - <dfn dfn>coverageMask</dfn> refers to multisample coverage mask (see [[#sample-masking]]).
    - <dfn dfn>depth</dfn> refers to the depth in [=viewport coordinates=],
        i.e. between the {{RenderState/[[viewport]]}} `minDepth` and `maxDepth`.
    - <dfn dfn>colors</dfn> refers to the list of color values,
        one for each target in {{GPURenderPassDescriptor/colorAttachments}}.
    - <dfn dfn>depthPassed</dfn>
        is `true` if the fragment passed the {{GPUDepthStencilState/depthCompare}} operation.
    - <dfn dfn>stencilPassed</dfn>
        is `true` if  the fragment passed the stencil {{GPUStencilFaceState/compare}} operation.
</div>

<div algorithm data-timeline=queue>
    <dfn abstract-op>process fragment</dfn>(rp, descriptor, state)

    **Arguments:**

    - |rp|: The [=RasterizationPoint=], produced by [[#rasterization]].
    - |descriptor|: The descriptor of type {{GPURenderPipelineDescriptor}}.
    - |state|: The active [=RenderState=].

    **Returns:** [=Fragment=] or `null`.

    1. Let |fragmentDesc| be |descriptor|.{{GPURenderPipelineDescriptor/fragment}}.
    1. Let |depthStencilDesc| be |descriptor|.{{GPURenderPipelineDescriptor/depthStencil}}.
    1. Let |fragment| be a new [=Fragment=] object.
    1. Set |fragment|.[=Fragment/destination=] to |rp|.[=RasterizationPoint/destination=].
    1. Set |fragment|.[=Fragment/frontFacing=] to |rp|.[=RasterizationPoint/frontFacing=].
    1. Set |fragment|.[=Fragment/coverageMask=] to |rp|.[=RasterizationPoint/coverageMask=].
    1. Set |fragment|.[=Fragment/depth=] to |rp|.[=RasterizationPoint/depth=].
    1. If `frag_depth` [=builtin=] is not produced by the shader:
        1. Set |fragment|.[=Fragment/depthPassed=] to the result of [$compare fragment$](|fragment|.[=Fragment/destination=],
            |fragment|.[=Fragment/depth=], "[=aspect/depth=]", |state|.{{RenderState/[[depthStencilAttachment]]}},
            |depthStencilDesc|?.{{GPUDepthStencilState/depthCompare}}).
    1. Set |stencilState| to |depthStencilDesc|?.{{GPUDepthStencilState/stencilFront}} if
        |rp|.[=RasterizationPoint/frontFacing=] is `true` and |depthStencilDesc|?.{{GPUDepthStencilState/stencilBack}}
        otherwise.
    1. Set |fragment|.[=Fragment/stencilPassed=] to the result of [$compare fragment$](|fragment|.[=Fragment/destination=],
        |state|.{{RenderState/[[stencilReference]]}}, "[=aspect/stencil=]", |state|.{{RenderState/[[depthStencilAttachment]]}},
        |stencilState|?.{{GPUStencilFaceState/compare}}).
    1. If |fragmentDesc| is not `null`:
        1. If |fragment|.[=Fragment/depthPassed=] is `false`, the `frag_depth` [=builtin=] is not produced by the
            shader entry point, and the shader entry point does not write to any [=internal usage/storage=] bindings,
            the following steps may be skipped.
        1. Set the shader input [=builtins=]. For each non-composite argument of the entry point,
            annotated as a [=builtin=], set its value based on the annotation:

            <dl class=switch>
                : `position`
                :: `vec4<f32>`(|rp|.[=RasterizationPoint/destination=].[=FragmentDestination/position=], |rp|.[=RasterizationPoint/depth=], |rp|.[=RasterizationPoint/perspectiveDivisor=])

                : `front_facing`
                :: |rp|.[=RasterizationPoint/frontFacing=]

                : `sample_index`
                :: |rp|.[=RasterizationPoint/destination=].[=FragmentDestination/sampleIndex=]

                : `sample_mask`
                :: |rp|.[=RasterizationPoint/coverageMask=]
            </dl>
        1. For each user-specified [=shader stage input=] of the fragment stage:
            1. Let |value| be the interpolated fragment input,
                based on |rp|.[=RasterizationPoint/barycentricCoordinates=], |rp|.[=RasterizationPoint/primitiveVertices=],
                and the [=interpolation=] qualifier on the input.
            1. Set the corresponding fragment shader [=location=] input to |value|.
        1. Invoke the fragment shader entry point described by |fragmentDesc|.

            The [=device=] may become [=lose the device|lost=] if
            [=shader execution end|shader execution does not end=]
            in a reasonable amount of time, as determined by the user agent.

        1. If the fragment issued `discard`, return `null`.
        1. Set |fragment|.[=Fragment/colors=] to the user-specified [=shader stage output=] values from the shader.
        1. Take the shader output [=builtins=]:
            1. If `frag_depth` [=builtin=] is produced by the shader as |value|:
                1. Let |vp| be |state|.{{RenderState/[[viewport]]}}.
                1. Set |fragment|.[=Fragment/depth=] to clamp(|value|, |vp|.`minDepth`, |vp|.`maxDepth`).
                1. Set |fragment|.[=Fragment/depthPassed=] to the result of [$compare fragment$](|fragment|.[=Fragment/destination=],
                    |fragment|.[=Fragment/depth=], "[=aspect/depth=]", |state|.{{RenderState/[[depthStencilAttachment]]}},
                    |depthStencilDesc|?.{{GPUDepthStencilState/depthCompare}}).
        1. If `sample_mask` [=builtin=] is produced by the shader as |value|:
            1. Set |fragment|.[=Fragment/coverageMask=] to |fragment|.[=Fragment/coverageMask=] &and; |value|.

        Otherwise we are in [[#no-color-output]] mode, and |fragment|.[=Fragment/colors=] is empty.
    1. Return |fragment|.
</div>

<div algorithm data-timeline=queue>
    <dfn abstract-op>compare fragment</dfn>(destination, value, aspect, attachment, compareFunc)

    **Arguments:**

    - |destination|: The [=FragmentDestination=].
    - |value|: The value to be compared.
    - |aspect|: The [=aspect=] of |attachment| to sample values from.
    - |attachment|: The attachment to be compared against.
    - |compareFunc|: The {{GPUCompareFunction}} to use, or `undefined`.

    **Returns:** `true` if the comparison passes, or `false` otherwise

    - If |attachment| is `undefined` or does not have |aspect|, return `true`.
    - If |compareFunc| is `undefined` or {{GPUCompareFunction/"always"}}, return `true`.
    - Let |attachmentValue| be the value of |aspect| of |attachment| at |destination|.
    - Return `true` if comparing |value| with |attachmentValue| using |compareFunc| succeeds, and `false` otherwise.
</div>

Processing of fragments happens in parallel, while any side effects,
such as writes into {{GPUBufferBindingType}} {{GPUBufferBindingType/"storage"}} bindings,
may happen in any order.

### Output Merging ### {#output-merging}

Output merging is a fixed-function stage of the render [=pipeline=] that
outputs the fragment color, depth and stencil data to be written into the render pass attachments.

<div algorithm data-timeline=queue>
    <dfn abstract-op>process depth stencil</dfn>(fragment, pipeline, state)

    **Arguments:**

    - |fragment|: The [=Fragment=], produced by [[#fragment-processing]].
    - |pipeline|: The current {{GPURenderPipeline}}.
    - |state|: The active [=RenderState=].

    1. Let |depthStencilDesc| be |pipeline|.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/depthStencil}}.

    1. If |pipeline|.{{GPURenderPipeline/[[writesDepth]]}} is `true` and |fragment|.[=Fragment/depthPassed=] is `true`:
        1. Set the value of the depth aspect of |state|.{{RenderState/[[depthStencilAttachment]]}} at
            |fragment|.[=Fragment/destination=] to |fragment|.[=Fragment/depth=].

    1. If |pipeline|.{{GPURenderPipeline/[[writesStencil]]}} is true:
        1. Set |stencilState| to |depthStencilDesc|.{{GPUDepthStencilState/stencilFront}} if
            |fragment|.[=Fragment/frontFacing=] is `true` and
            |depthStencilDesc|.{{GPUDepthStencilState/stencilBack}} otherwise.
        1. If |fragment|.[=Fragment/stencilPassed=] is `false`:
            - Let |stencilOp| be |stencilState|.{{GPUStencilFaceState/failOp}}.

            Otherwise, if |fragment|.[=Fragment/depthPassed=] is `false`:
            - Let |stencilOp| be |stencilState|.{{GPUStencilFaceState/depthFailOp}}.

            Otherwise:
            - Let |stencilOp| be |stencilState|.{{GPUStencilFaceState/passOp}}.
        1. Update the value of the stencil aspect of |state|.{{RenderState/[[depthStencilAttachment]]}} at
            |fragment|.[=Fragment/destination=] by performing the operation described by |stencilOp|.
</div>

The depth input to this stage, if any, is clamped to the current {{RenderState/[[viewport]]}} depth
range (regardless of whether the fragment shader stage writes the `frag_depth` builtin).

<div algorithm data-timeline=queue>
    <dfn abstract-op>process color attachments</dfn>(fragment, pipeline, state)

    **Arguments:**

    - |fragment|: The [=Fragment=], produced by [[#fragment-processing]].
    - |pipeline|: The current {{GPURenderPipeline}}.
    - |state|: The active [=RenderState=].

    1. If |fragment|.[=Fragment/depthPassed=] is `false` or |fragment|.[=Fragment/stencilPassed=] is `false`, return.

    1. Let |targets| be |pipeline|.{{GPURenderPipeline/[[descriptor]]}}.{{GPURenderPipelineDescriptor/fragment}}.{{GPUFragmentState/targets}}.
    1. For each |attachment| of |state|.{{RenderState/[[colorAttachments]]}}:
        1. Let |color| be the value from |fragment|.[=Fragment/colors=] that corresponds with |attachment|.
        1. Let |targetDesc| be the |targets| entry that corresponds with |attachment|.

        1. If |targetDesc|.{{GPUColorTargetState/blend}} is [=map/exist|provided=]:
            1. Let |colorBlend| be |targetDesc|.{{GPUColorTargetState/blend}}.{{GPUBlendState/color}}.
            1. Let |alphaBlend| be |targetDesc|.{{GPUColorTargetState/blend}}.{{GPUBlendState/alpha}}.
            1. Set the RGB components of |color| to the value computed by performing the operation described by
                |colorBlend|.{{GPUBlendComponent/operation}} with the values described by
                |colorBlend|.{{GPUBlendComponent/srcFactor}} and |colorBlend|.{{GPUBlendComponent/dstFactor}}.
            1. Set the alpha component of |color| to the value computed by performing the operation described by
                |alphaBlend|.{{GPUBlendComponent/operation}} with the values described by
                |alphaBlend|.{{GPUBlendComponent/srcFactor}} and |alphaBlend|.{{GPUBlendComponent/dstFactor}}.

        1. Set the value of |attachment| at |fragment|.[=Fragment/destination=] to |color|.
</div>

### No Color Output ### {#no-color-output}

In no-color-output mode, [=pipeline=] does not produce any color attachment outputs.

The [=pipeline=] still performs rasterization and produces depth values
based on the vertex position output. The depth testing and stencil operations can still be used.

### Alpha to Coverage ### {#alpha-to-coverage}

In alpha-to-coverage mode, an additional <dfn dfn>alpha-to-coverage mask</dfn>
of MSAA samples is generated based on the |alpha| component of the
fragment shader output value at `@location(0)`.

The algorithm of producing the extra mask is platform-dependent and can vary for different pixels.
It guarantees that:

- if |alpha| &le; 0.0, the result is 0x0
- if |alpha| &ge; 1.0, the result is 0xFFFFFFFF
- intermediate |alpha| values should result in a proportionate number of bits set to 1 in the mask.
    Some platforms may not guarantee that the number of bits set to 1 in the mask monotonically
    increases as alpha increases for a given pixel.

<h4 id=per-sample-shading>Per-Sample Shading
<span id=sample-frequency-shading></span>
</h4>

When rendering into multisampled render attachments, fragment shaders can be run once per-pixel or once per-sample.
Fragment shaders **must** run once per-sample if either the `sample_index` [=builtin=] or `sample` [=interpolation sampling=]
is used and contributes to the shader output. Otherwise fragment shaders **may** run once per-pixel with the result
broadcast out to each of the samples included in the [=final sample mask=].

When using per-sample shading, the color output for sample |N| is produced by the fragment shader execution
with `sample_index` == |N| for the current pixel.

### Sample Masking ### {#sample-masking}

The <dfn dfn>final sample mask</dfn> for a pixel is computed as:
[=rasterization mask=] & {{GPUMultisampleState/mask}} & [=shader-output mask=].

Only the lower {{GPUMultisampleState/count}} bits of the mask are considered.

If the least-significant bit at position |N| of the [=final sample mask=] has value of "0",
the sample color outputs (corresponding to sample |N|) to all attachments of the fragment shader are discarded.
Also, no depth test or stencil operations are executed on the relevant samples of the depth-stencil attachment.

The <dfn dfn>rasterization mask</dfn> is produced by the rasterization stage,
based on the shape of the rasterized polygon. The samples included in the shape get the relevant
bits 1 in the mask.

The <dfn dfn>shader-output mask</dfn> takes the output value of "sample_mask" [=builtin=] in the fragment shader.
If the builtin is not output from the fragment shader, and {{GPUMultisampleState/alphaToCoverageEnabled}}
is enabled, the [=shader-output mask=] becomes the [=alpha-to-coverage mask=]. Otherwise, it defaults to 0xFFFFFFFF.

# Type Definitions # {#type-definitions}

<script type=idl>
typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
typedef [EnforceRange] unsigned long GPUStencilValue;
typedef [EnforceRange] unsigned long GPUSampleMask;
typedef [EnforceRange] long GPUDepthBias;

typedef [EnforceRange] unsigned long long GPUSize64;
typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
typedef [EnforceRange] unsigned long GPUIndex32;
typedef [EnforceRange] unsigned long GPUSize32;
typedef [EnforceRange] long GPUSignedOffset32;

typedef unsigned long long GPUSize64Out;
typedef unsigned long GPUIntegerCoordinateOut;
typedef unsigned long GPUSize32Out;

typedef unsigned long GPUFlagsConstant;
</script>

## Colors &amp; Vectors ## {#colors-and-vectors}

<script type=idl>
dictionary GPUColorDict {
    required double r;
    required double g;
    required double b;
    required double a;
};
typedef (sequence<double> or GPUColorDict) GPUColor;
</script>

Note: `double` is large enough to precisely hold 32-bit signed/unsigned
integers and single-precision floats.

<dl dfn-type=dict-member dfn-for=GPUColorDict>
    : <dfn>r</dfn>
    ::
        The red channel value.

    : <dfn>g</dfn>
    ::
        The green channel value.

    : <dfn>b</dfn>
    ::
        The blue channel value.

    : <dfn>a</dfn>
    ::
        The alpha channel value.
</dl>

<div algorithm="GPUColor accessors" dfn-for=GPUColor>
    For a given {{GPUColor}} value |color|, depending on its type, the syntax:

    - |color|.<dfn dfn noexport>r</dfn> refers to
        either {{GPUColorDict}}.{{GPUColorDict/r}}
        or the first item of the sequence ([=assert|asserting=] there is such an item).
    - |color|.<dfn dfn noexport>g</dfn> refers to
        either {{GPUColorDict}}.{{GPUColorDict/g}}
        or the second item of the sequence ([=assert|asserting=] there is such an item).
    - |color|.<dfn dfn noexport>b</dfn> refers to
        either {{GPUColorDict}}.{{GPUColorDict/b}}
        or the third item of the sequence ([=assert|asserting=] there is such an item).
    - |color|.<dfn dfn noexport>a</dfn> refers to
        either {{GPUColorDict}}.{{GPUColorDict/a}}
        or the fourth item of the sequence ([=assert|asserting=] there is such an item).
</div>
<div algorithm data-timeline=content>
    <dfn abstract-op>validate GPUColor shape</dfn>(color)

    **Arguments:**

    - |color|: The {{GPUColor}} to validate.

    **Returns:** {{undefined}}

    [=Content timeline=] steps:

    1. Throw a {{TypeError}} if |color| is a sequence and |color|.[=list/size=] &ne; 4.
</div>

<script type=idl>
dictionary GPUOrigin2DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;
</script>

<div algorithm="GPUOrigin2D accessors" dfn-for=GPUOrigin2D>
    For a given {{GPUOrigin2D}} value |origin|, depending on its type, the syntax:

    - |origin|.<dfn dfn noexport>x</dfn> refers to
        either {{GPUOrigin2DDict}}.{{GPUOrigin2DDict/x}}
        or the first item of the sequence (0 if not present).
    - |origin|.<dfn dfn noexport>y</dfn> refers to
        either {{GPUOrigin2DDict}}.{{GPUOrigin2DDict/y}}
        or the second item of the sequence (0 if not present).
</div>
<div algorithm data-timeline=content>
    <dfn abstract-op>validate GPUOrigin2D shape</dfn>(origin)

    **Arguments:**

    - |origin|: The {{GPUOrigin2D}} to validate.

    **Returns:** {{undefined}}

    [=Content timeline=] steps:

    1. Throw a {{TypeError}} if |origin| is a sequence and |origin|.[=list/size=] &gt; 2.
</div>

<script type=idl>
dictionary GPUOrigin3DDict {
    GPUIntegerCoordinate x = 0;
    GPUIntegerCoordinate y = 0;
    GPUIntegerCoordinate z = 0;
};
typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
</script>

<div algorithm="GPUOrigin3D accessors" dfn-for=GPUOrigin3D>
    For a given {{GPUOrigin3D}} value |origin|, depending on its type, the syntax:

    - |origin|.<dfn dfn>x</dfn> refers to
        either {{GPUOrigin3DDict}}.{{GPUOrigin3DDict/x}}
        or the first item of the sequence (0 if not present).
    - |origin|.<dfn dfn>y</dfn> refers to
        either {{GPUOrigin3DDict}}.{{GPUOrigin3DDict/y}}
        or the second item of the sequence (0 if not present).
    - |origin|.<dfn dfn>z</dfn> refers to
        either {{GPUOrigin3DDict}}.{{GPUOrigin3DDict/z}}
        or the third item of the sequence (0 if not present).
</div>
<div algorithm data-timeline=content>
    <dfn abstract-op>validate GPUOrigin3D shape</dfn>(origin)

    **Arguments:**

    - |origin|: The {{GPUOrigin3D}} to validate.

    **Returns:** {{undefined}}

    [=Content timeline=] steps:

    1. Throw a {{TypeError}} if |origin| is a sequence and |origin|.[=list/size=] &gt; 3.
</div>

<script type=idl>
dictionary GPUExtent3DDict {
    required GPUIntegerCoordinate width;
    GPUIntegerCoordinate height = 1;
    GPUIntegerCoordinate depthOrArrayLayers = 1;
};
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
</script>

<dl dfn-type=dict-member dfn-for=GPUExtent3DDict>
    : <dfn>width</dfn>
    ::
        The width of the extent.

    : <dfn>height</dfn>
    ::
        The height of the extent.

    : <dfn>depthOrArrayLayers</dfn>
    ::
        The depth of the extent or the number of array layers it contains.
        If used with a {{GPUTexture}} with a {{GPUTextureDimension}} of {{GPUTextureDimension/"3d"}}
        defines the depth of the texture. If used with a {{GPUTexture}} with a {{GPUTextureDimension}}
        of {{GPUTextureDimension/"2d"}} defines the number of array layers in the texture.
</dl>

<div algorithm="GPUExtent3D accessors" dfn-for=GPUExtent3D>
    For a given {{GPUExtent3D}} value |extent|, depending on its type, the syntax:

    - |extent|.<dfn dfn>width</dfn> refers to
        either {{GPUExtent3DDict}}.{{GPUExtent3DDict/width}}
        or the first item of the sequence ([=assert|asserting=] there is such an item).
    - |extent|.<dfn dfn>height</dfn> refers to
        either {{GPUExtent3DDict}}.{{GPUExtent3DDict/height}}
        or the second item of the sequence (1 if not present).
    - |extent|.<dfn dfn>depthOrArrayLayers</dfn> refers to
        either {{GPUExtent3DDict}}.{{GPUExtent3DDict/depthOrArrayLayers}}
        or the third item of the sequence (1 if not present).
</div>
<div algorithm data-timeline=content>
    <dfn abstract-op>validate GPUExtent3D shape</dfn>(extent)

    **Arguments:**

    - |extent|: The {{GPUExtent3D}} to validate.

    **Returns:** {{undefined}}

    [=Content timeline=] steps:

    1. Throw a {{TypeError}} if:

       - |extent| is a sequence, and
       - |extent|.[=list/size=] &lt; 1 or |extent|.[=list/size=] &gt; 3.
</div>

# Feature Index # {#feature-index}

<h3 id=core-features-and-limits data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"core-features-and-limits"`
</h3>

Allows all Core WebGPU features and limits to be used.

Note: This is currently available on all adapters and enabled automatically on all devices even if not requested.

<h3 id=depth-clip-control data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"depth-clip-control"`
<span id=dom-gpufeaturename-depth-clip-control></span>
</h3>

Allows [=depth clipping=] to be disabled.

This feature adds the following [=optional API surfaces=]:

- New {{GPUPrimitiveState}} dictionary members:
    - {{GPUPrimitiveState/unclippedDepth}}

<h3 id=depth32float-stencil8 data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"depth32float-stencil8"`
<span id=dom-gpufeaturename-depth32float-stencil8></span>
</h3>

Allows for explicit creation of textures of format {{GPUTextureFormat/"depth32float-stencil8"}}.

This feature adds the following [=optional API surfaces=]:

- New {{GPUTextureFormat}} enum values:
    - {{GPUTextureFormat/"depth32float-stencil8"}}

<h3 id=texture-compression-bc data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-compression-bc"`
<span id=dom-gpufeaturename-texture-compression-bc></span>
</h3>

Allows for explicit creation of textures of [=BC compressed formats=] which include the "S3TC",
"RGTC", and "BPTC" formats. Only supports 2D textures.

Note: Adapters which support {{GPUFeatureName/"texture-compression-bc"}} do not
always support {{GPUFeatureName/"texture-compression-bc-sliced-3d"}}.
To use {{GPUFeatureName/"texture-compression-bc-sliced-3d"}},
{{GPUFeatureName/"texture-compression-bc"}} must be enabled explicitly as this feature
does not enable the BC formats.

This feature adds the following [=optional API surfaces=]:

- New {{GPUTextureFormat}} enum values:
    - {{GPUTextureFormat/"bc1-rgba-unorm"}}
    - {{GPUTextureFormat/"bc1-rgba-unorm-srgb"}}
    - {{GPUTextureFormat/"bc2-rgba-unorm"}}
    - {{GPUTextureFormat/"bc2-rgba-unorm-srgb"}}
    - {{GPUTextureFormat/"bc3-rgba-unorm"}}
    - {{GPUTextureFormat/"bc3-rgba-unorm-srgb"}}
    - {{GPUTextureFormat/"bc4-r-unorm"}}
    - {{GPUTextureFormat/"bc4-r-snorm"}}
    - {{GPUTextureFormat/"bc5-rg-unorm"}}
    - {{GPUTextureFormat/"bc5-rg-snorm"}}
    - {{GPUTextureFormat/"bc6h-rgb-ufloat"}}
    - {{GPUTextureFormat/"bc6h-rgb-float"}}
    - {{GPUTextureFormat/"bc7-rgba-unorm"}}
    - {{GPUTextureFormat/"bc7-rgba-unorm-srgb"}}

<h3 id=texture-compression-bc-sliced-3d data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-compression-bc-sliced-3d"`
<span id=dom-gpufeaturename-texture-compression-bc-sliced-3d></span>
</h3>

Allows the {{GPUTextureDimension/3d}} dimension for textures with [=BC compressed formats=].

Note: Adapters which support {{GPUFeatureName/"texture-compression-bc"}} do not
always support {{GPUFeatureName/"texture-compression-bc-sliced-3d"}}.
To use {{GPUFeatureName/"texture-compression-bc-sliced-3d"}},
{{GPUFeatureName/"texture-compression-bc"}} must be enabled explicitly as this feature
does not enable the BC formats.

This feature adds no [=optional API surfaces=].

<h3 id=texture-compression-etc2 data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-compression-etc2"`
<span id=texture-compression-etc></span>
<span id=dom-gpufeaturename-texture-compression-etc2></span>
</h3>

Allows for explicit creation of textures of [=ETC2 compressed formats=]. Only supports 2D textures.

This feature adds the following [=optional API surfaces=]:

- New {{GPUTextureFormat}} enum values:
    - {{GPUTextureFormat/"etc2-rgb8unorm"}}
    - {{GPUTextureFormat/"etc2-rgb8unorm-srgb"}}
    - {{GPUTextureFormat/"etc2-rgb8a1unorm"}}
    - {{GPUTextureFormat/"etc2-rgb8a1unorm-srgb"}}
    - {{GPUTextureFormat/"etc2-rgba8unorm"}}
    - {{GPUTextureFormat/"etc2-rgba8unorm-srgb"}}
    - {{GPUTextureFormat/"eac-r11unorm"}}
    - {{GPUTextureFormat/"eac-r11snorm"}}
    - {{GPUTextureFormat/"eac-rg11unorm"}}
    - {{GPUTextureFormat/"eac-rg11snorm"}}

<h3 id=texture-compression-astc data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-compression-astc"`
<span id=dom-gpufeaturename-texture-compression-astc></span>
</h3>

Allows for explicit creation of textures of [=ASTC compressed formats=]. Only supports 2D textures.

This feature adds the following [=optional API surfaces=]:

- New {{GPUTextureFormat}} enum values:
    - {{GPUTextureFormat/"astc-4x4-unorm"}}
    - {{GPUTextureFormat/"astc-4x4-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-5x4-unorm"}}
    - {{GPUTextureFormat/"astc-5x4-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-5x5-unorm"}}
    - {{GPUTextureFormat/"astc-5x5-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-6x5-unorm"}}
    - {{GPUTextureFormat/"astc-6x5-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-6x6-unorm"}}
    - {{GPUTextureFormat/"astc-6x6-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-8x5-unorm"}}
    - {{GPUTextureFormat/"astc-8x5-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-8x6-unorm"}}
    - {{GPUTextureFormat/"astc-8x6-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-8x8-unorm"}}
    - {{GPUTextureFormat/"astc-8x8-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-10x5-unorm"}}
    - {{GPUTextureFormat/"astc-10x5-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-10x6-unorm"}}
    - {{GPUTextureFormat/"astc-10x6-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-10x8-unorm"}}
    - {{GPUTextureFormat/"astc-10x8-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-10x10-unorm"}}
    - {{GPUTextureFormat/"astc-10x10-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-12x10-unorm"}}
    - {{GPUTextureFormat/"astc-12x10-unorm-srgb"}}
    - {{GPUTextureFormat/"astc-12x12-unorm"}}
    - {{GPUTextureFormat/"astc-12x12-unorm-srgb"}}

<h3 id=texture-compression-astc-sliced-3d data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-compression-astc-sliced-3d"`
<span id=dom-gpufeaturename-texture-compression-astc-sliced-3d></span>
</h3>

Allows the {{GPUTextureDimension/3d}} dimension for textures with [=ASTC compressed formats=].

Note: Adapters which support {{GPUFeatureName/"texture-compression-astc"}} do not
always support {{GPUFeatureName/"texture-compression-astc-sliced-3d"}}.
To use {{GPUFeatureName/"texture-compression-astc-sliced-3d"}},
{{GPUFeatureName/"texture-compression-astc"}} must be enabled explicitly as this feature
does not enable the ASTC formats.

This feature adds no [=optional API surfaces=].

<h3 id=timestamp-query data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"timestamp-query"`
<span id=dom-gpufeaturename-timestamp-query></span>
</h3>

Adds the ability to query timestamps from GPU command buffers. See [[#timestamp]].

This feature adds the following [=optional API surfaces=]:

- New {{GPUQueryType}} values:
    - {{GPUQueryType/"timestamp"}}
- New {{GPUComputePassDescriptor}} members:
    - {{GPUComputePassDescriptor/timestampWrites}}
- New {{GPURenderPassDescriptor}} members:
    - {{GPURenderPassDescriptor/timestampWrites}}

<h3 id=indirect-first-instance data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"indirect-first-instance"`
<span id=dom-gpufeaturename-indirect-first-instance></span>
</h3>

Allows the use of non-zero `firstInstance` values in [=indirect draw parameters=] and [=indirect drawIndexed parameters=].

This feature adds no [=optional API surfaces=].

<h3 id=shader-f16 data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"shader-f16"`
<span id=dom-gpufeaturename-shader-f16></span>
</h3>

Allows the use of the half-precision floating-point type [=f16=] in WGSL.

This feature adds the following [=optional API surfaces=]:

- New WGSL extensions:
    - [=extension/f16=]

<h3 id=rg11b10ufloat-renderable data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"rg11b10ufloat-renderable"`
<span id=dom-gpufeaturename-rg11b10ufloat-renderable></span>
</h3>

Allows the {{GPUTextureUsage/RENDER_ATTACHMENT}} usage on textures with format {{GPUTextureFormat/"rg11b10ufloat"}},
and also allows textures of that format to be blended, multisampled, and resolved.

This feature adds no [=optional API surfaces=].

Enabling {{GPUFeatureName/"texture-formats-tier1"}} at device creation will also enable
{{GPUFeatureName/"rg11b10ufloat-renderable"}}.

<h3 id=bgra8unorm-storage data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"bgra8unorm-storage"`
</h3>

Allows the {{GPUTextureUsage/STORAGE_BINDING}} usage on textures with format {{GPUTextureFormat/"bgra8unorm"}}.

This feature adds no [=optional API surfaces=].

<h3 id=float32-filterable data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"float32-filterable"`
</h3>

Makes textures with formats {{GPUTextureFormat/"r32float"}}, {{GPUTextureFormat/"rg32float"}}, and
{{GPUTextureFormat/"rgba32float"}} [=filterable=].

<h3 id=float32-blendable data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"float32-blendable"`
</h3>

Makes textures with formats {{GPUTextureFormat/"r32float"}}, {{GPUTextureFormat/"rg32float"}}, and
{{GPUTextureFormat/"rgba32float"}} [=blendable=].

<h3 id=dom-gpufeaturename-clip-distances data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"clip-distances"`
</h3>

Allows the use of [=builtin/clip_distances=] in WGSL.

This feature adds the following [=optional API surfaces=]:

- New WGSL extensions:
    - [=extension/clip_distances=]

<h3 id=dom-gpufeaturename-dual-source-blending data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"dual-source-blending"`
</h3>

Allows the use of [=blend_src=] in WGSL and simultaneously using both pixel shader outputs
(`@blend_src(0)` and `@blend_src(1)`) as inputs to a blending operation with the single color
attachment at [=location=] `0`.

This feature adds the following [=optional API surfaces=]:
- Allows the use of the below {{GPUBlendFactor}}s:
    - {{GPUBlendFactor/"src1"}}
    - {{GPUBlendFactor/"one-minus-src1"}}
    - {{GPUBlendFactor/"src1-alpha"}}
    - {{GPUBlendFactor/"one-minus-src1-alpha"}}

- New WGSL extensions:
    - [=extension/dual_source_blending=]

<h3 id=subgroups data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"subgroups"`
<span id=dom-gpufeaturename-subgroups></span>
</h3>

Allows the use of the subgroup and quad operations in WGSL.

This feature adds no [=optional API surfaces=], but the following entries of {{GPUAdapterInfo}}
expose real values whenever the feature is available on the adapter:
- {{GPUAdapterInfo/subgroupMinSize}}
- {{GPUAdapterInfo/subgroupMaxSize}}

- New WGSL extensions:
    - [=extension/subgroups=]

<h3 id=texture-formats-tier1 data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-formats-tier1"`
</h3>

Supports the below new {{GPUTextureFormat}}s with the {{GPUTextureUsage/RENDER_ATTACHMENT}},
[=blendable=], `multisampling` capabilities and the {{GPUTextureUsage/STORAGE_BINDING}} capability
with the {{GPUStorageTextureAccess/"read-only"}} and {{GPUStorageTextureAccess/"write-only"}}
{{GPUStorageTextureAccess}}es:
- {{GPUTextureFormat/"r16unorm"}}
- {{GPUTextureFormat/"r16snorm"}}
- {{GPUTextureFormat/"rg16unorm"}}
- {{GPUTextureFormat/"rg16snorm"}}
- {{GPUTextureFormat/"rgba16unorm"}}
- {{GPUTextureFormat/"rgba16snorm"}}

Allows the {{GPUTextureUsage/RENDER_ATTACHMENT}}, [=blendable=], `multisampling` and `resolve`
capabilities on below {{GPUTextureFormat}}s:
- {{GPUTextureFormat/"r8snorm"}}
- {{GPUTextureFormat/"rg8snorm"}}
- {{GPUTextureFormat/"rgba8snorm"}}

Allows the {{GPUStorageTextureAccess/"read-only"}} or {{GPUStorageTextureAccess/"write-only"}}
{{GPUStorageTextureAccess}} on below {{GPUTextureFormat}}s:
- {{GPUTextureFormat/"r8unorm"}}
- {{GPUTextureFormat/"r8snorm"}}
- {{GPUTextureFormat/"r8uint"}}
- {{GPUTextureFormat/"r8sint"}}
- {{GPUTextureFormat/"rg8unorm"}}
- {{GPUTextureFormat/"rg8snorm"}}
- {{GPUTextureFormat/"rg8uint"}}
- {{GPUTextureFormat/"rg8sint"}}
- {{GPUTextureFormat/"r16uint"}}
- {{GPUTextureFormat/"r16sint"}}
- {{GPUTextureFormat/"r16float"}}
- {{GPUTextureFormat/"rg16uint"}}
- {{GPUTextureFormat/"rg16sint"}}
- {{GPUTextureFormat/"rg16float"}}
- {{GPUTextureFormat/"rgb10a2uint"}}
- {{GPUTextureFormat/"rgb10a2unorm"}}
- {{GPUTextureFormat/"rg11b10ufloat"}}

Enabling {{GPUFeatureName/"texture-formats-tier2"}} at device creation will also enable
{{GPUFeatureName/"texture-formats-tier1"}}.

Enabling {{GPUFeatureName/"texture-formats-tier1"}} at device creation will also enable
{{GPUFeatureName/"rg11b10ufloat-renderable"}}.

<h3 id=texture-formats-tier2 data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"texture-formats-tier2"`
</h3>

Allows the {{GPUStorageTextureAccess/"read-write"}} {{GPUStorageTextureAccess}} on below
{{GPUTextureFormat}}s:
- {{GPUTextureFormat/"r8unorm"}}
- {{GPUTextureFormat/"r8uint"}}
- {{GPUTextureFormat/"r8sint"}}
- {{GPUTextureFormat/"rgba8unorm"}}
- {{GPUTextureFormat/"rgba8uint"}}
- {{GPUTextureFormat/"rgba8sint"}}
- {{GPUTextureFormat/"r16uint"}}
- {{GPUTextureFormat/"r16sint"}}
- {{GPUTextureFormat/"r16float"}}
- {{GPUTextureFormat/"rgba16uint"}}
- {{GPUTextureFormat/"rgba16sint"}}
- {{GPUTextureFormat/"rgba16float"}}
- {{GPUTextureFormat/"rgba32uint"}}
- {{GPUTextureFormat/"rgba32sint"}}
- {{GPUTextureFormat/"rgba32float"}}

Enabling {{GPUFeatureName/"texture-formats-tier2"}} at device creation will also enable
{{GPUFeatureName/"texture-formats-tier1"}}.

<h3 id=dom-gpufeaturename-primitive-index data-dfn-type=enum-value data-dfn-for=GPUFeatureName>`"primitive-index"`
</h3>

Allows the use of [=builtin/primitive_index=] in WGSL.

This feature adds the following [=optional API surfaces=]:

- New WGSL extensions:
    - [=extension/primitive_index=]

# Appendices # {#appendices}

## Texture Format Capabilities ## {#texture-format-caps}

### Plain color formats ### {#plain-color-formats}

All [$Validate texture format required features|supported$] plain color formats support usages
{{GPUTextureUsage/COPY_SRC}}, {{GPUTextureUsage/COPY_DST}}, and
{{GPUTextureUsage/TEXTURE_BINDING}}, and dimension {{GPUTextureDimension/"3d"}}.

The {{GPUTextureUsage/RENDER_ATTACHMENT}} and {{GPUTextureUsage/STORAGE_BINDING}} columns
specify support for {{GPUTextureUsage/RENDER_ATTACHMENT|GPUTextureUsage.RENDER_ATTACHMENT}}
and {{GPUTextureUsage/STORAGE_BINDING|GPUTextureUsage.STORAGE_BINDING}} usage respectively.

The <dfn dfn>render target pixel byte cost</dfn>
and <dfn dfn>render target component alignment</dfn>
are used to validate the {{supported limits/maxColorAttachmentBytesPerSample}} limit.

Note:
The [=texel block memory cost=] of each of these formats is the same as its
[=texel block copy footprint=].

<table class=data>
    <thead class=stickyheader>
        <tr>
            <th rowspan=2>Format
            <th rowspan=2>Required [=Feature=]
            <th rowspan=2>{{GPUTextureSampleType}}
            <th rowspan=2 class=vertical><span>{{GPUTextureUsage/RENDER_ATTACHMENT}}</span>
            <th rowspan=2 class=vertical><span>[=blendable=]</span>
            <th rowspan=2 class=vertical><span>multisampling</span>
            <th rowspan=2 class=vertical><span>resolve</span>
            <th colspan=3>{{GPUTextureUsage/STORAGE_BINDING}}
            <th rowspan=2>[=Texel block copy footprint=] (Bytes)
            <th rowspan=2>[=Render target pixel byte cost=] (Bytes)
        <tr>
            <th class=vertical><span>{{GPUStorageTextureAccess/"write-only"}}</span>
            <th class=vertical><span>{{GPUStorageTextureAccess/"read-only"}}</span>
            <th class=vertical><span>{{GPUStorageTextureAccess/"read-write"}}</span>
    </thead>
    <tr><th colspan=12>8 bits per component (1-byte [=render target component alignment=])
    <tr>
        <td>{{GPUTextureFormat/r8unorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>1
    <tr>
        <td>{{GPUTextureFormat/r8snorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=6>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td><!-- Vulkan -->
        <td>1
        <td>&ndash; <!-- no render target -->
    <tr>
        <td>{{GPUTextureFormat/r8uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>1
    <tr>
        <td>{{GPUTextureFormat/r8sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>1
    <tr>
        <td>{{GPUTextureFormat/rg8unorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td><!-- Vulkan -->
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/rg8snorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=6>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td><!-- Vulkan -->
        <td>2
        <td>&ndash; <!-- no render target -->
    <tr>
        <td>{{GPUTextureFormat/rg8uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/rg8sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/rgba8unorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td>4
        <td>8
    <tr>
        <td>{{GPUTextureFormat/rgba8unorm-srgb}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td>
        <td>
        <td>4
        <td>8
    <tr>
        <td>{{GPUTextureFormat/rgba8snorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=4>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td>4
        <td>&ndash; <!-- no render target --> <!-- If we add render target support for this in the future, rgba8snorm has to be a special case where the render target pixel byte cost = 8 -->
    <tr>
        <td>{{GPUTextureFormat/rgba8uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rgba8sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/bgra8unorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"bgra8unorm-storage"}} is enabled
        <td>
        <td>
        <td>4
        <td>8
    <tr>
        <td>{{GPUTextureFormat/bgra8unorm-srgb}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td>
        <td>
        <td>4
        <td>8
    <tr><th colspan=12>16 bits per component (2-byte [=render target component alignment=])
    <tr>
        <td>{{GPUTextureFormat/r16unorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/r16snorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/r16uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/r16sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/r16float}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>2
    <tr>
        <td>{{GPUTextureFormat/rg16unorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rg16snorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rg16uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rg16sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rg16float}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td><!-- Vulkan -->
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rgba16unorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rgba16snorm}}
        <td>{{GPUFeatureName/"texture-formats-tier1"}}
        <td>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rgba16uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rgba16sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rgba16float}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>8
    <tr><th colspan=12>32 bits per component (4-byte [=render target component alignment=])
    <tr>
        <td>{{GPUTextureFormat/r32uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/r32sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/r32float}}
        <td>
        <td>
            <p>{{GPUTextureSampleType/"float"}} if {{GPUFeatureName/"float32-filterable"}} is enabled
            <p>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>If {{GPUFeatureName/"float32-blendable"}} is enabled
        <td>&checkmark;
        <td><!-- Metal -->
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>4
    <tr>
        <td>{{GPUTextureFormat/rg32uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rg32sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rg32float}}
        <td>
        <td>
            <p>{{GPUTextureSampleType/"float"}} if {{GPUFeatureName/"float32-filterable"}} is enabled
            <p>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>If {{GPUFeatureName/"float32-blendable"}} is enabled
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>
        <td colspan=2>8
    <tr>
        <td>{{GPUTextureFormat/rgba32uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>16
    <tr>
        <td>{{GPUTextureFormat/rgba32sint}}
        <td>
        <td>{{GPUTextureSampleType/"sint"}}
        <td>&checkmark;
        <td>
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>16
    <tr>
        <td>{{GPUTextureFormat/rgba32float}}
        <td>
        <td>
            <p>{{GPUTextureSampleType/"float"}} if {{GPUFeatureName/"float32-filterable"}} is enabled
            <p>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>If {{GPUFeatureName/"float32-blendable"}} is enabled
        <td><!-- Metal -->
        <td>
        <td>&checkmark;
        <td>&checkmark;
        <td>If {{GPUFeatureName/"texture-formats-tier2"}} is enabled
        <td colspan=2>16
    <tr><th colspan=12>mixed component width, 32 bits per texel (4-byte [=render target component alignment=])
    <tr>
        <td>{{GPUTextureFormat/rgb10a2uint}}
        <td>
        <td>{{GPUTextureSampleType/"uint"}}
        <td>&checkmark;
        <td>
        <td>&checkmark;
        <td>
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td>4
        <td>8
    <tr>
        <td>{{GPUTextureFormat/rgb10a2unorm}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td>&checkmark;
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td>
        <td>4
        <td>8
    <tr>
        <td>{{GPUTextureFormat/rg11b10ufloat}}
        <td>
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=4>If {{GPUFeatureName/"rg11b10ufloat-renderable"}} is enabled
        <td colspan=2>If {{GPUFeatureName/"texture-formats-tier1"}} is enabled
        <td><!-- Vulkan -->
        <td>4
        <td>8
</table>

### Depth-stencil formats ### {#depth-formats}

A <dfn dfn>depth-or-stencil format</dfn> is any format with depth and/or stencil aspects.
A <dfn dfn>combined depth-stencil format</dfn> is a [=depth-or-stencil format=] that has both
depth and stencil aspects.

All [=depth-or-stencil formats=] support the {{GPUTextureUsage/COPY_SRC}}, {{GPUTextureUsage/COPY_DST}},
{{GPUTextureUsage/TEXTURE_BINDING}}, and {{GPUTextureUsage/RENDER_ATTACHMENT}} usages.
All of these formats support multisampling.
However, certain copy operations also restrict the source and destination formats, and none of
these formats support textures with {{GPUTextureDimension/"3d"}} dimension.

Depth textures cannot be used with {{GPUSamplerBindingType/"filtering"}} samplers, but can always
be used with {{GPUSamplerBindingType/"comparison"}} samplers even if they use filtering.

<table class=data>
    <thead>
        <tr>
            <th>Format
            <th class=note heading>[=Texel block memory cost=] (Bytes)
            <th>Aspect
            <th>{{GPUTextureSampleType}}
            <th>Valid [=texel copy=] source
            <th>Valid [=texel copy=] destination
            <th>[=Texel block copy footprint=] (Bytes)
            <th><dfn dfn>Aspect-specific format</dfn>
    </thead>
    <tr>
        <td>{{GPUTextureFormat/stencil8}}
        <td>1 &minus; 4
        <td>stencil
        <td>{{GPUTextureSampleType/"uint"}}
        <td colspan=2>&checkmark;
        <td>1
        <td>{{GPUTextureFormat/stencil8}}
    <tr>
        <td>{{GPUTextureFormat/depth16unorm}}
        <td>2
        <td>depth
        <td>{{GPUTextureSampleType/"depth"}}, {{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=2>&checkmark;
        <td>2
        <td>{{GPUTextureFormat/depth16unorm}}
    <tr>
        <td>{{GPUTextureFormat/depth24plus}}
        <td>4
        <td>depth
        <td>{{GPUTextureSampleType/"depth"}}, {{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=2>&cross;
        <td>&ndash;
        <td>{{GPUTextureFormat/depth24plus}}
    <tr>
        <td rowspan=2 style="white-space: nowrap">{{GPUTextureFormat/depth24plus-stencil8}}
        <td rowspan=2>4 &minus; 8
        <td>depth
        <td>{{GPUTextureSampleType/"depth"}}, {{GPUTextureSampleType/"unfilterable-float"}}
        <td colspan=2>&cross;
        <td>&ndash;
        <td>{{GPUTextureFormat/depth24plus}}
    <tr>
        <td>stencil
        <td>{{GPUTextureSampleType/"uint"}}
        <td colspan=2>&checkmark;
        <td>1
        <td>{{GPUTextureFormat/stencil8}}
    <tr>
        <td>{{GPUTextureFormat/depth32float}}
        <td>4
        <td>depth
        <td>{{GPUTextureSampleType/"depth"}}, {{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&cross;
        <td>4
        <td>{{GPUTextureFormat/depth32float}}
    <tr>
        <td rowspan=2 style="white-space: nowrap">{{GPUTextureFormat/depth32float-stencil8}}
        <td rowspan=2>5 &minus; 8
        <td>depth
        <td>{{GPUTextureSampleType/"depth"}}, {{GPUTextureSampleType/"unfilterable-float"}}
        <td>&checkmark;
        <td>&cross;
        <td>4
        <td>{{GPUTextureFormat/depth32float}}
    <tr>
        <td>stencil
        <td>{{GPUTextureSampleType/"uint"}}
        <td colspan=2>&checkmark;
        <td>1
        <td>{{GPUTextureFormat/stencil8}}
</table>

<dfn dfn>24-bit depth</dfn> refers to a 24-bit unsigned normalized depth format with a range from
0.0 to 1.0, which would be spelled "depth24unorm" if exposed.

#### Reading and Sampling Depth/Stencil Textures #### {#reading-depth-stencil}

It is [$validating shader binding|possible$] to bind a depth-aspect {{GPUTextureView}}
to either a `texture_depth_*` binding or a binding with other non-depth 2d/cube texture types.

A stencil-aspect {{GPUTextureView}} must be bound to a normal texture binding type.
The {{GPUTextureBindingLayout/sampleType}} in the {{GPUBindGroupLayout}}
must be {{GPUTextureSampleType/"uint"}}.

Reading or sampling the depth or stencil aspect of a texture behaves as if the texture contains
the values `(V, X, X, X)`, where V is the actual depth or stencil value,
and each X is an [=implementation-defined=] unspecified value.

For depth-aspect bindings, the unspecified values are not visible through bindings with
`texture_depth_*` types.

<div class=example>
    If a depth texture is bound to `tex` with type `texture_2d<f32>`:

    - `textureSample(tex, ...)` will return `vec4<f32>(D, X, X, X)`.
    - `textureGather(0, tex, ...)` will return `vec4<f32>(D1, D2, D3, D4)`.
    - `textureGather(2, tex, ...)` will return `vec4<f32>(X1, X2, X3, X4)` (a completely unspecified value).
</div>

Note:
Short of adding a new more constrained stencil sampler type (like depth), it's infeasible for
implementations to efficiently paper over the driver differences for depth/stencil reads.
As this was not a portability pain point for WebGL, it's not expected to be problematic in WebGPU.
In practice, expect either `(V, V, V, V)` or `(V, 0, 0, 1)` (where `V` is the depth or stencil
value), depending on hardware.

#### Copying Depth/Stencil Textures #### {#copying-depth-stencil}

The depth aspects of depth32float formats
({{GPUTextureFormat/"depth32float"}} and {{GPUTextureFormat/"depth32float-stencil8"}}
have a limited range.
As a result, copies into such textures are only valid from other textures of the same format.
<!-- POSTV1(unrestricted-depth): unless unrestricted depth is enabled -->

The depth aspects of depth24plus formats
({{GPUTextureFormat/"depth24plus"}} and {{GPUTextureFormat/"depth24plus-stencil8"}})
have opaque representations (implemented as either [=24-bit depth=] or {{GPUTextureFormat/"depth32float"}}).
As a result, depth-aspect [=texel copies=] are not allowed with these formats.

<div class=note heading>
    It is possible to imitate these disallowed copies:

    - All of these formats can be written in a render pass using a fragment shader that outputs
        depth values via the `frag_depth` output.
    - Textures with "depth24plus" formats can be read as shader textures, and
        written to a texture (as a render pass attachment) or
        buffer (via a storage buffer binding in a compute shader).
</div>

### Packed formats ### {#packed-formats}

All packed texture formats support {{GPUTextureUsage/COPY_SRC}}, {{GPUTextureUsage/COPY_DST}},
and {{GPUTextureUsage/TEXTURE_BINDING}} usages.
All of these formats are [=filterable=].
None of these formats are [=renderable=] or support multisampling.

A <dfn dfn>compressed format</dfn> is any format with a block size greater than 1&times;1.

Note:
The [=texel block memory cost=] of each of these formats is the same as its
[=texel block copy footprint=].

<table class=data>
    <thead class=stickyheader>
        <tr>
            <th>Format
            <th>[=Texel block copy footprint=] (Bytes)
            <th>{{GPUTextureSampleType}}
            <th>Texel block [=texel block width|width=]/[=texel block height|height=]
            <th>{{GPUTextureDimension/"3d"}}
            <th>[=Feature=]
    </thead>
    <tr>
        <td>{{GPUTextureFormat/rgb9e5ufloat}}
        <td>4
        <td>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td>1 &times; 1
        <td>&check;
        <td>
    <tr>
        <td>{{GPUTextureFormat/bc1-rgba-unorm}}
        <td rowspan=2>8
        <td rowspan=14>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td rowspan=14>4 &times; 4
        <td rowspan=14>If {{GPUFeatureName/"texture-compression-bc-sliced-3d"}} is enabled
        <td rowspan=14>{{GPUFeatureName/texture-compression-bc}}
    <tr>
        <td>{{GPUTextureFormat/bc1-rgba-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/bc2-rgba-unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/bc2-rgba-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/bc3-rgba-unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/bc3-rgba-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/bc4-r-unorm}}
        <td rowspan=2>8
    <tr>
        <td>{{GPUTextureFormat/bc4-r-snorm}}
    <tr>
        <td>{{GPUTextureFormat/bc5-rg-unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/bc5-rg-snorm}}
    <tr>
        <td>{{GPUTextureFormat/bc6h-rgb-ufloat}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/bc6h-rgb-float}}
    <tr>
        <td>{{GPUTextureFormat/bc7-rgba-unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/bc7-rgba-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/etc2-rgb8unorm}}
        <td rowspan=2>8
        <td rowspan=10>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td rowspan=10>4 &times; 4
        <td rowspan=10>
        <td rowspan=10>{{GPUFeatureName/texture-compression-etc2}}
    <tr>
        <td>{{GPUTextureFormat/etc2-rgb8unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/etc2-rgb8a1unorm}}
        <td rowspan=2>8
    <tr>
        <td>{{GPUTextureFormat/etc2-rgb8a1unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/etc2-rgba8unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/etc2-rgba8unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/eac-r11unorm}}
        <td rowspan=2>8
    <tr>
        <td>{{GPUTextureFormat/eac-r11snorm}}
    <tr>
        <td>{{GPUTextureFormat/eac-rg11unorm}}
        <td rowspan=2>16
    <tr>
        <td>{{GPUTextureFormat/eac-rg11snorm}}
    <tr>
        <td>{{GPUTextureFormat/astc-4x4-unorm}}
        <td rowspan=2>16
        <td rowspan=28>{{GPUTextureSampleType/"float"}},<br/>{{GPUTextureSampleType/"unfilterable-float"}}
        <td rowspan=2>4 &times; 4
        <td rowspan=28>If {{GPUFeatureName/"texture-compression-astc-sliced-3d"}} is enabled
        <td rowspan=28>{{GPUFeatureName/texture-compression-astc}}
    <tr>
        <td>{{GPUTextureFormat/astc-4x4-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-5x4-unorm}}
        <td rowspan=2>16
        <td rowspan=2>5 &times; 4
    <tr>
        <td>{{GPUTextureFormat/astc-5x4-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-5x5-unorm}}
        <td rowspan=2>16
        <td rowspan=2>5 &times; 5
    <tr>
        <td>{{GPUTextureFormat/astc-5x5-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-6x5-unorm}}
        <td rowspan=2>16
        <td rowspan=2>6 &times; 5
    <tr>
        <td>{{GPUTextureFormat/astc-6x5-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-6x6-unorm}}
        <td rowspan=2>16
        <td rowspan=2>6 &times; 6
    <tr>
        <td>{{GPUTextureFormat/astc-6x6-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-8x5-unorm}}
        <td rowspan=2>16
        <td rowspan=2>8 &times; 5
    <tr>
        <td>{{GPUTextureFormat/astc-8x5-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-8x6-unorm}}
        <td rowspan=2>16
        <td rowspan=2>8 &times; 6
    <tr>
        <td>{{GPUTextureFormat/astc-8x6-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-8x8-unorm}}
        <td rowspan=2>16
        <td rowspan=2>8 &times; 8
    <tr>
        <td>{{GPUTextureFormat/astc-8x8-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-10x5-unorm}}
        <td rowspan=2>16
        <td rowspan=2>10 &times; 5
    <tr>
        <td>{{GPUTextureFormat/astc-10x5-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-10x6-unorm}}
        <td rowspan=2>16
        <td rowspan=2>10 &times; 6
    <tr>
        <td>{{GPUTextureFormat/astc-10x6-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-10x8-unorm}}
        <td rowspan=2>16
        <td rowspan=2>10 &times; 8
    <tr>
        <td>{{GPUTextureFormat/astc-10x8-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-10x10-unorm}}
        <td rowspan=2>16
        <td rowspan=2>10 &times; 10
    <tr>
        <td>{{GPUTextureFormat/astc-10x10-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-12x10-unorm}}
        <td rowspan=2>16
        <td rowspan=2>12 &times; 10
    <tr>
        <td>{{GPUTextureFormat/astc-12x10-unorm-srgb}}
    <tr>
        <td>{{GPUTextureFormat/astc-12x12-unorm}}
        <td rowspan=2>16
        <td rowspan=2>12 &times; 12
    <tr>
        <td>{{GPUTextureFormat/astc-12x12-unorm-srgb}}
</table>

<script>
// Small script to collect all xrefs that refer to different timelines and highlight
// them with an appropriate style.

const dataLinks = document.querySelectorAll("[data-link-type='abstract-op'], [data-link-type='dfn'], [data-link-type='idl'], [data-link-type='attribute']");
for (const dataLink of dataLinks) {
    const linkUrl = dataLink.getAttribute('href');
    if (linkUrl && linkUrl.startsWith('#')) {
      const dataLinkTarget = document.getElementById(linkUrl.substring(1));
      if (dataLinkTarget) {
          // Find the closest ancestor of the target (including the target itself)
          // that contains a data-timeline.
          const timelineElement = dataLinkTarget.closest('[data-timeline]');
          if (timelineElement) {
            // If we found a timeline, apply an appropriate style the link.
            const timeline = timelineElement.getAttribute('data-timeline');
            dataLink.setAttribute('data-timeline', timeline);
          }
      }
    }
}
</script>
